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

# Authentication

All endpoints use HMAC-SHA256 signature-based authentication.

#### Required Headers

All requests must include the following headers:

* `X-Esim-Story-Access-Key`: Your partner access key
* `X-Esim-Story-Signature`: HMAC-SHA256 signature of the request
* `X-Esim-Story-Timestamp`: Unix timestamp in seconds (UTC)

#### Signature Generation

The signature is generated using HMAC-SHA256 with your Base64-encoded secret key. Follow these steps:

1. **Decode your secret key** from Base64
2. **Construct the raw string** using newline characters (`\n`):
   ```
   {HTTP_METHOD}\n{REQUEST_PATH}\n{TIMESTAMP}\n{ACCESS_KEY}
   ```
3. **Generate HMAC-SHA256** signature using the decoded secret key
4. **Convert to hexadecimal** string (lowercase)

**Important Notes:**

* Use the exact HTTP method in uppercase (e.g., `POST`, `GET`)
* Use the exact request path (e.g., `/api/v1/api_partner/orders`) - **do not include query parameters or domain**
* Timestamp must be Unix timestamp in seconds (not milliseconds)
* All components must be joined with newline characters (`\n`)

#### Example

<CodeGroup>
  ```bash cURL theme={null}
  #!/bin/bash

  # Configuration
  ACCESS_KEY="your_access_key"
  SECRET_KEY="your_base64_encoded_secret_key"

  # Generate timestamp (Unix seconds)
  TIMESTAMP=$(date +%s)

  # Request details
  METHOD="POST"
  PATH="/api/v1/api_partner/orders"

  # Construct raw string
  RAW_STRING="${METHOD}\n${PATH}\n${TIMESTAMP}\n${ACCESS_KEY}"

  # Decode secret key and generate signature
  DECODED_SECRET=$(echo -n "$SECRET_KEY" | base64 -d)
  SIGNATURE=$(echo -n -e "$RAW_STRING" | openssl dgst -sha256 -hmac "$DECODED_SECRET" | cut -d' ' -f2)

  # Make request
  curl -X POST https://api.esimstory.com/api/v1/api_partner/orders \
    -H "Content-Type: application/json" \
    -H "X-Esim-Story-Access-Key: ${ACCESS_KEY}" \
    -H "X-Esim-Story-Signature: ${SIGNATURE}" \
    -H "X-Esim-Story-Timestamp: ${TIMESTAMP}" \
    -d '{
      "external_order_id": "1234567890",
      "products": [
        {
          "option_id": "686ffd73-61af-ee11-be9e-002248f7dbdd",
          "qty": 1
        }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const crypto = require("crypto");

  function generateSignature(method, path, timestamp, accessKey, secretKey) {
    // Construct raw string with newlines
    const rawString = `${method}\n${path}\n${timestamp}\n${accessKey}`;

    // Decode Base64 secret key
    const decodedSecret = Buffer.from(secretKey, "base64");

    // Generate HMAC-SHA256 signature
    const signature = crypto
      .createHmac("sha256", decodedSecret)
      .update(rawString)
      .digest("hex");

    return signature;
  }

  // Generate timestamp (Unix seconds)
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const method = "POST";
  const path = "/api/v1/api_partner/orders";
  const signature = generateSignature(
    method,
    path,
    timestamp,
    accessKey,
    secretKey,
  );

  const response = await fetch(
    "https://api.esimstory.com/api/v1/api_partner/orders",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Esim-Story-Access-Key": accessKey,
        "X-Esim-Story-Signature": signature,
        "X-Esim-Story-Timestamp": timestamp,
      },
      body: JSON.stringify({
        external_order_id: "1234567890",
        products: [
          {
            option_id: "686ffd73-61af-ee11-be9e-002248f7dbdd",
            qty: 1,
          },
        ],
      }),
    },
  );
  ```

  ```python Python theme={null}
  import hmac
  import hashlib
  import base64
  import time

  def generate_signature(method, path, timestamp, access_key, secret_key):
      # Construct raw string with newlines
      raw_string = f'{method}\n{path}\n{timestamp}\n{access_key}'

      # Decode Base64 secret key
      decoded_secret = base64.b64decode(secret_key)

      # Generate HMAC-SHA256 signature
      signature = hmac.new(
          decoded_secret,
          raw_string.encode('utf-8'),
          hashlib.sha256
      ).hexdigest()

      return signature

  # Generate timestamp (Unix seconds)
  timestamp = str(int(time.time()))
  method = 'POST'
  path = '/api/v1/api_partner/orders'
  signature = generate_signature(method, path, timestamp, access_key, secret_key)

  headers = {
      'Content-Type': 'application/json',
      'X-Esim-Story-Access-Key': access_key,
      'X-Esim-Story-Signature': signature,
      'X-Esim-Story-Timestamp': timestamp
  }
  ```

  ```ruby Ruby theme={null}
  require 'openssl'
  require 'base64'
  require 'time'

  def generate_signature(method, path, timestamp, access_key, secret_key)
    # Construct raw string with newlines
    raw_string = "#{method}\n#{path}\n#{timestamp}\n#{access_key}"

    # Decode Base64 secret key
    decoded_secret = Base64.decode64(secret_key)

    # Generate HMAC-SHA256 signature
    OpenSSL::HMAC.hexdigest('SHA256', decoded_secret, raw_string)
  end

  # Generate timestamp (Unix seconds)
  timestamp = Time.now.to_i.to_s
  method = 'POST'
  path = '/api/v1/api_partner/orders'
  signature = generate_signature(method, path, timestamp, access_key, secret_key)

  headers = {
    'Content-Type' => 'application/json',
    'X-Esim-Story-Access-Key' => access_key,
    'X-Esim-Story-Signature' => signature,
    'X-Esim-Story-Timestamp' => timestamp
  }
  ```
</CodeGroup>

#### Authentication Flow

The server validates your request by:

1. **Verifying required headers** - All authentication headers must be present
2. **Validating timestamp** - Timestamp must be within a limited time window to prevent replay attacks
3. **Verifying signature** - Server recreates the signature using your access key and compares it with the provided signature using secure comparison methods

#### Security Requirements

* **Timestamp Validation**: The timestamp must be within a limited time window of the current UTC time to prevent replay attacks
* **Secret Key**: Your secret key is stored Base64-encoded and must be decoded before use in signature generation
* **Path**: Use the exact request path (e.g., `/api/v1/api_partner/orders`) - **do not include**:
  * Query parameters
  * Domain name
  * Protocol
* **Method**: Use the exact HTTP method in uppercase (e.g., `POST`, `GET`)
* **Newline Characters**: The raw string must use actual newline characters (`\n`), not literal `\n` strings

#### Error Responses

If authentication fails, you'll receive a `401 Unauthorized` response:

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid signature."
  }
}
```

**Error Messages:**

* `"Missing required authentication headers."` - One or more required headers (`X-Esim-Story-Access-Key`, `X-Esim-Story-Signature`, `X-Esim-Story-Timestamp`) are missing
* `"Request timestamp is too old or invalid."` - Timestamp is outside the allowed time window or invalid format
* `"Invalid or missing access key. Please provide a valid X-Esim-Story-Access-Key header."` - Access key is incorrect or partner not found
* `"Missing secret key in partner record."` - Partner exists but has no secret key configured
* `"Invalid signature."` - Signature verification failed (most common cause: incorrect raw string construction or secret key)
