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

# Reference

## Status and error codes

Order statuses (`data.status`): `CONFIRMED`, `PARTIALLY_CANCELLED`, `CANCELLED`, `DECLINED`.

| HTTP  | Meaning                                                                                         |
| ----- | ----------------------------------------------------------------------------------------------- |
| `200` | applied (or a safe retry) — the body has the current order state                                |
| `401` | auth failed — missing header, expired timestamp, bad signature, or inactive key                 |
| `422` | invalid data or an illegal change (e.g. a refund larger than the order, a confirm after cancel) |
| `409` | write collision — retry with the same `event_id`                                                |
| `429` | too many requests — slow down and retry                                                         |

***

## Ready-to-paste signing helpers

Each of these computes the signature exactly as our server checks it. The one rule they all follow: **serialize the body once, sign that exact string, send those exact bytes.**

<CodeGroup>
  ```php PHP theme={"dark"}
  <?php
  function signAndPost(string $base, string $apiKey, string $secret, string $path, array $payload): array
  {
      $ts   = (string) time();
      $body = json_encode($payload, JSON_UNESCAPED_UNICODE); // serialize ONCE
      $sig  = hash_hmac('sha256', $ts . $body, $secret);

      $ch = curl_init($base . $path);
      curl_setopt_array($ch, [
          CURLOPT_POST           => true,
          CURLOPT_POSTFIELDS     => $body,   // the SAME bytes we signed
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER     => [
              'Content-Type: application/json',
              'X-Merchant-Key: ' . $apiKey,
              'X-Signature-Timestamp: ' . $ts,
              'X-Signature: ' . $sig,
          ],
      ]);
      $res  = curl_exec($ch);
      $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
      curl_close($ch);

      return ['status' => $code, 'body' => json_decode($res, true)];
  }

  $out = signAndPost('https://realry.com/api/v1/direct', 'mk_your_key', 'your_hmac_secret',
      '/conversions', [
          'merchant_order_id' => 'order-1',
          'event_id'          => 'evt-1',
          'amount'            => 49900,
          'currency'          => 'KRW',
          'cb_aev'            => $clickId,
          'product_name'      => 'Silk Blouse',
      ]);
  ```

  ```js Node.js theme={"dark"}
  import crypto from 'node:crypto';

  async function signAndPost(base, apiKey, secret, path, payload) {
    const ts = Math.floor(Date.now() / 1000).toString();
    const body = JSON.stringify(payload);           // serialize ONCE
    const sig = crypto.createHmac('sha256', secret).update(ts + body).digest('hex');

    const res = await fetch(base + path, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Merchant-Key': apiKey,
        'X-Signature-Timestamp': ts,
        'X-Signature': sig,
      },
      body,                                          // the SAME string we signed
    });
    return { status: res.status, body: await res.json() };
  }

  await signAndPost('https://realry.com/api/v1/direct', 'mk_your_key', 'your_hmac_secret',
    '/conversions', {
      merchant_order_id: 'order-1',
      event_id: 'evt-1',
      amount: 49900,
      currency: 'KRW',
      cb_aev: clickId,
      product_name: 'Silk Blouse',
    });
  ```

  ```python Python theme={"dark"}
  import hashlib, hmac, json, time, urllib.request

  def sign_and_post(base, api_key, secret, path, payload):
      ts = str(int(time.time()))
      body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)  # serialize ONCE
      sig = hmac.new(secret.encode(), (ts + body).encode(), hashlib.sha256).hexdigest()

      req = urllib.request.Request(
          base + path,
          data=body.encode(),
          method="POST",
          headers={
              "Content-Type": "application/json",
              "X-Merchant-Key": api_key,
              "X-Signature-Timestamp": ts,
              "X-Signature": sig,
          },
      )
      with urllib.request.urlopen(req) as r:
          return {"status": r.status, "body": json.loads(r.read())}

  sign_and_post("https://realry.com/api/v1/direct", "mk_your_key", "your_hmac_secret",
      "/conversions", {
          "merchant_order_id": "order-1",
          "event_id": "evt-1",
          "amount": 49900,
          "currency": "KRW",
          "cb_aev": click_id,
          "product_name": "Silk Blouse",
      })
  ```

  ```bash cURL theme={"dark"}
  BASE="https://realry.com/api/v1/direct"
  API_KEY="mk_your_key"; SECRET="your_hmac_secret"

  TS=$(date +%s)
  BODY='{"merchant_order_id":"order-1","event_id":"evt-1","amount":49900,"currency":"KRW","cb_aev":"ABC123","product_name":"Silk Blouse"}'
  SIG=$(printf '%s' "${TS}${BODY}" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

  curl -sS -X POST "$BASE/conversions" \\
    -H "X-Merchant-Key: $API_KEY" \\
    -H "X-Signature-Timestamp: $TS" \\
    -H "X-Signature: $SIG" \\
    -H "Content-Type: application/json" \\
    --data "$BODY"
  ```
</CodeGroup>

<Warning>
  **Stuck on a `401 invalid_signature`?** It almost always means the bytes you signed differ from the bytes you sent — usually the body got re-encoded or its keys reordered after signing. Build the body once, sign it, send it unchanged.
</Warning>

***

Questions? Reach out to your Aeris partner contact. Always test with a sandbox key first, then ask us to issue your live credential.
