> ## Documentation Index
> Fetch the complete documentation index at: https://docs.c2cpayment.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication Process

> Authenticate every request with your merchant API key, and sign deposit and withdrawal orders with the key's signing secret.

## Overview

The C2C Merchant API uses two credentials, issued together:

| Credential | Format | Used for |
| - | - | - |
| **API key** | `mk_live_` + 32 alphanumeric characters | Identifies your merchant account. Sent in the `C2C-API-Key` header on every request. |
| **Signing secret** | `sksec_` + 40 alphanumeric characters | Signs deposit and withdrawal orders and verifies the [webhooks](/guides/webhooks) C2C sends you. Never sent over the wire. See the [Signature Generation Guide](/api-reference/signature-guide). |

<Warning>
  Both credentials are shown **only once**, when C2C issues them. C2C stores only a hash of your API key and cannot show it to you again. Store both immediately in a secret manager or environment variables, and never expose them in client-side code.
</Warning>

## Getting your credentials

A C2C administrator issues API keys for your merchant account, and each key comes with its own signing secret. In the **Merchant Portal** you can see your keys (name, prefix, status, last used) and revoke a key, but you can't create new ones there. Contact C2C to get a key or to rotate one.

* You can hold **more than one active API key** (for example one per backend service). Each key has its own signing secret.
* **Idempotency keys** are scoped to the API key that used them. **Orders** belong to your merchant account: any of your active keys can query their status, including orders created before a key rotation.
* **Regenerating** your API key issues a new key and secret and **revokes every previously active key** on your account.
* A revoked key stops working immediately.

<Note>
  What you can do in the Merchant Portal depends on your portal role. The account owner has full access. Team members only see the pages their role allows: **API Keys** to view or revoke keys, **Settings** to change the default callback URL, and **Transactions** for order history. The account owner manages roles in the portal.
</Note>

## Authentication method

Send your API key in the `C2C-API-Key` header:

```http theme={null}
C2C-API-Key: mk_live_your_api_key
```

<Note>
  `Authorization: Bearer mk_live_...` is still accepted for existing integrations. If a request carries both, `C2C-API-Key` is used.
</Note>

### Required headers

| Header | Required on | Value |
| - | - | - |
| `C2C-API-Key` | All requests | `mk_live_...` |
| `Content-Type` | Requests with a body | `application/json` |
| `Idempotency-Key` | `POST /api/merchant/orders` | Your unique key for this order attempt, up to 100 characters. See [Idempotency](/api-reference/idempotency). |

Deposit and withdrawal requests also carry a `signature` field **in the JSON body**. See the [Signature Generation Guide](/api-reference/signature-guide). Status and balance requests need only the API key.

### Example request

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.c2cpayment.net/api/merchant/orders/balance \
    --header "C2C-API-Key: $C2C_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://api.c2cpayment.net/api/merchant/orders/balance',
    { headers: { 'C2C-API-Key': process.env.C2C_API_KEY } }
  );
  console.log(response.status, await response.json());
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.get(
      "https://api.c2cpayment.net/api/merchant/orders/balance",
      headers={"C2C-API-Key": os.environ["C2C_API_KEY"]},
  )
  print(response.status_code, response.json())
  ```
</CodeGroup>

A successful call returns `200`:

```json theme={null}
{
  "data": {
    "availableBalance": 0.0,
    "reservedBalance": 0.0,
    "frozenBalance": 0.0,
    "currency": "PKR"
  }
}
```

<Tip>
  `GET /api/merchant/orders/balance` has no side effects, so it is a good way to test your API key. Test your signing code against the [worked examples](/api-reference/signature-guide#worked-examples).
</Tip>

## Storing credentials securely

<CodeGroup>
  ```bash Linux/macOS theme={null}
  export C2C_API_KEY="mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  export C2C_SIGNING_SECRET="sksec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```powershell Windows PowerShell theme={null}
  $env:C2C_API_KEY="mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  $env:C2C_SIGNING_SECRET="sksec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```bash .env file theme={null}
  # Add .env to .gitignore
  C2C_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  C2C_SIGNING_SECRET=sksec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  ```
</CodeGroup>

<Steps>
  <Step title="Server-side only">
    Call the C2C API from your backend. Never ship the API key or signing secret to a browser or mobile app.
  </Step>

  <Step title="Use a secret manager in production">
    Azure Key Vault, AWS Secrets Manager or HashiCorp Vault, not plain configuration files.
  </Step>

  <Step title="Never log credentials">
    Mask the `C2C-API-Key` (or `Authorization`) header and never log the signing secret.
  </Step>

  <Step title="Separate keys per environment">
    Use different keys for development and production. Keys only work in the environment that issued them.
  </Step>

  <Step title="Rotate on exposure">
    If a key or secret may have leaked, contact C2C to revoke it and issue a replacement.
  </Step>
</Steps>

## Account-level access controls

Besides the API key and signature, C2C applies these checks when you create an order. The Merchant API has no request rate limits.

| Check | Result when it fails |
| - | - |
| Merchant account is **Active** | `403`: `Merchant account is suspended; new orders are not accepted.` |
| **IP whitelist**, if C2C has enabled one for your account (Deposit / Withdrawal scope) | `403`: `IP not in whitelist.` |
| Payment method (`walletType`) **enabled** for your account and order type | `422`: `JazzCash deposits are disabled for this merchant.` |

## Authentication errors

### 401 Unauthorized: API key

Returned when no API key is sent (neither `C2C-API-Key` nor `Authorization: Bearer mk_...`), the `C2C-API-Key` value isn't an `mk_` key, or the key is unknown or revoked. The response has an **empty body**.

```http theme={null}
HTTP/1.1 401 Unauthorized
Content-Length: 0
```

**Checklist**

1. The header is exactly `C2C-API-Key: mk_live_...`, with no quotes or extra spaces.
2. The key belongs to the environment you are calling.
3. The key has not been revoked or replaced by a regeneration.

### 401 Unauthorized: signature

Returned on order creation when the API key is valid but the body's `signature` is missing, malformed, wrong or replayed. Unlike an API key failure, this response has a JSON body with a `code`:

```json theme={null}
{
  "success": false,
  "error": "The request body must include a 'signature' field.",
  "code": "SIGNATURE_REQUIRED",
  "traceId": "15f1bf9c3472631576b915f2382451fd"
}
```

See [Signature error responses](/api-reference/signature-guide#signature-error-responses).

## Next steps

<CardGroup cols={2}>
  <Card title="Signature Generation Guide" icon="signature" href="/api-reference/signature-guide">
    Sign requests correctly
  </Card>

  <Card title="Create a deposit" icon="arrow-down-to-line" href="/api-reference/endpoints/create-deposit">
    Create your first order
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/api-reference/errors">
    Every error response the API returns
  </Card>

  <Card title="Quickstart" icon="rocket" href="/quickstart">
    End-to-end integration
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.