# 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 find your API key in the Hands In merchant dashboard.

👉 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**](https://merchant.handsin.com/dashboard/developers/webhooks).

* **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?
