# API Authentication (/docs/guides/authentication/authentication-overview) 

# Overview [#overview]

Hands In authenticates all API requests using **merchant API keys**. Each merchant account is issued a unique API key via the Hands In dashboard, and this key must be included in every request.

Think of your API key as a **secret identifier** for your integration — similar to a password. Keep it secure and **never expose it publicly**.

## Using the `x-api-key` Header [#using-the-x-api-key-header]

To authenticate a request, include your API key in a `x-api-key` HTTP header field.

For example, if your API key is `abcd1234`, your request headers would include it like so:

```http
POST /v1/multi-card-payments HTTP/1.1
Host: api.sandbox.handsin.com
Content-Type: application/json
Accept: application/json
x-api-key: abcd1234
```

### See examples below [#see-examples-below]

<Tabs items="[&#x22;curl&#x22;, &#x22;Node.js (fetch)&#x22;, &#x22;Python (requests)&#x22;, &#x22;PowerShell&#x22;]">
  <Tab value="curl">
    ```bash
    curl --request POST \
      --url https://api.sandbox.handsin.com/v1/multi-card-payments \
      --header "Accept: application/json" \
      --header "Content-Type: application/json" \
      --header "x-api-key: <your-api-key>" \
      --data '{
        "amountMoney": {
          "currency": "GBP",
          "amount": 50000
        },
        "idempotencyKey": "<unique-random-string>"
      }'
    ```
  </Tab>

  <Tab value="Node.js (fetch)">
    ```javascript
    const url = "https://api.sandbox.handsin.com/v1/multi-card-payments";

    const payload = {
      amountMoney: {
        currency: "GBP",
        amount: 50000
      },
      idempotencyKey: "<unique-random-string>"
    };

    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Content-Type": "application/json",
          "x-api-key": "<your-api-key>"
        },
        body: JSON.stringify(payload)
      });

      if (!response.ok) {
        throw new Error(`Response status: ${response.status}`);
      }

      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error("Request failed:", error.message);
    }
    ```
  </Tab>

  <Tab value="Python (requests)">
    ```python
    import requests

    url = "https://api.sandbox.handsin.com/v1/multi-card-payments"
    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "x-api-key": "<your-api-key>"
    }
    payload = {
        "amountMoney": {
            "currency": "GBP",
            "amount": 50000
        },
        "idempotencyKey": "<unique-random-string>"
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()
        print(response.json())
    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
    ```
  </Tab>

  <Tab value="PowerShell">
    ```powershell
    $headers = @{
      "Accept" = "application/json"
      "Content-Type" = "application/json"
      "x-api-key" = "<your-api-key>"
    }

    $body = @{
      amountMoney = @{
        currency = "GBP"
        amount = 50000
      }
      idempotencyKey = "<unique-random-string>"
    } | ConvertTo-Json -Depth 3

    try {
    $response = Invoke-RestMethod -Uri "https://api.sandbox.handsin.com/v1/multi-card-payments"
    -Method POST -Headers $headers -Body $body
    $response
    } catch {
    Write-Host "Request failed: $($_.Exception.Message)"
    }
    ```
  </Tab>
</Tabs>

The Hands In platform checks this header on every request and only allows access if the key is valid. You can create and manage your API keys in the [Hands In dashboard](https://app.handsin.com) under **Developers** > **API keys**.

👉 Refer to the [API Keys](/docs/guides/authentication/api-keys) for instructions on obtaining and managing your keys.

## Sandbox and Live Environments [#sandbox-and-live-environments]

Hands In provides two separate environments whilst integrating - testing (**Sandbox**) and production (**Live**).

| Environment | Base URL                             | Real Transactions | Purpose                                                              |
| ----------- | ------------------------------------ | ----------------- | -------------------------------------------------------------------- |
| Sandbox     | `https://api.sandbox.handsin.com/v1` | ❌ No              | Used for testing and development. No real payments are processed.    |
| Live        | `https://api.handsin.com/v1`         | ✅ Yes             | Used in production. All requests are processed as live transactions. |

***

#### Key Differences [#key-differences]

* **Authentication**: Each environment uses **different API keys**. Use your **Sandbox** key for sandbox URLs and your **Live** key for live URLs

* **Webhooks**: You'll need to configure webhooks separately for each environment via **Developers** > **Webhooks** in the [Hands In dashboard](https://app.handsin.com).

* **No Real Charges in Sandbox**: The Sandbox API simulates payment flows, and no live transactions are made.

* **Same API Structure**: Both environments use the same endpoints, request formats, and response structures — making it easy to move from testing to production.

***

## ❌ Authentication Errors [#-authentication-errors]

If a request is made without a valid API key, the API will return one of the following errors:

| Status Code        | Meaning                                                                                                                                                      |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `401 Unauthorized` | No valid API key was provided. The server received no valid credentials.                                                                                     |
| `403 Forbidden`    | The API key was provided but is not authorized to access or perform an action on the resource (e.g., wrong environment, invalid permissions or revoked key). |

### ✅ Troubleshooting Checklist [#-troubleshooting-checklist]

* Are you using the **correct API key** for the environment?
* Are you correctly setting the `x-api-key` header in your request?
