Get the app Map (opens in a new tab)List your business

Developers

Quickstart

your first sandbox order

From sign-up to your first successful sandbox order: keys, linking a test merchant, publishing a menu, receiving and accepting an order, going live.

Coming soonThe Partner API is not open yet. These docs are here so you can plan your integration; hosts and keys are issued at launch.Register as a developer →

On this page

What you will do

Six steps, all in the sandbox. No real money and no real customers.

  1. Sign up and get test keys.
  2. Set up signing.
  3. Link a test merchant.
  4. Publish a menu.
  5. Receive a test order, accept it and complete it.
  6. Go live.

1. Sign up and get test keys

  1. Open the developer dashboard at developers.aurages.com Coming soon and sign in to your partner account. At launch we create accounts ourselves; self-registration is coming soon.
  2. Under “Keys”, create a test key. You get a key id pk_test_xxx and a secret sk_test_xxx.
  3. The secret is shown once. Keep it on your server only, never in an app or a browser.
  4. Under “Webhooks”, add an endpoint on your server, such as https://pos.example.com/aurmap/webhooks, and save its signing secret whsec_xxx.

Your account comes with a ready test merchant: “Test restaurant” with two branches.

bash
export AURMAP_HOST="https://sandbox.partners.aurmap.com"
export AURMAP_KEY_ID="pk_test_xxx"
export AURMAP_KEY_SECRET="sk_test_xxx"

The sandbox address works now with sandbox keys. The live address opens for each developer once their integration is reviewed and approved.

2. Set up signing

Every request carries four headers: the key id, the time, a random nonce and an HMAC signature. Details in Keys and signing. Here is a ready bash function used by the examples on every page:

bash
aurmap() { # aurmap METHOD PATH [BODY]
  local method="$1" path="$2" body="${3:-}"
  local ts nonce bh sig
  ts=$(date +%s)
  nonce=$(openssl rand -hex 16)
  bh=$(printf '%s' "$body" | openssl dgst -sha256 -hex | sed 's/^.* //')
  sig=$(printf '%s\n%s\n%s\n%s\n%s' "$method" "$path" "$ts" "$nonce" "$bh" \
        | openssl dgst -sha256 -hmac "$AURMAP_KEY_SECRET" -hex | sed 's/^.* //')
  curl -sS -X "$method" "$AURMAP_HOST$path" \
    -H "Content-Type: application/json" \
    -H "X-AurMap-Key-Id: $AURMAP_KEY_ID" \
    -H "X-AurMap-Timestamp: $ts" \
    -H "X-AurMap-Nonce: $nonce" \
    -H "X-AurMap-Signature: $sig" \
    -H "Idempotency-Key: ${IDEMPOTENCY_KEY:-$(openssl rand -hex 12)}" \
    ${body:+--data-raw "$body"}
}

The same in Node:

js
import crypto from 'node:crypto';

const { AURMAP_HOST, AURMAP_KEY_ID, AURMAP_KEY_SECRET } = process.env;

export function sign(method, path, ts, nonce, body) {
  const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
  const canonical = [method.toUpperCase(), path, ts, nonce, bodyHash].join('\n');
  return crypto.createHmac('sha256', AURMAP_KEY_SECRET).update(canonical).digest('hex');
}

export async function aurmap(method, path, payload, idempotencyKey) {
  const body = payload === undefined ? '' : JSON.stringify(payload);
  const ts = String(Math.floor(Date.now() / 1000));
  const nonce = crypto.randomBytes(16).toString('hex');
  const headers = {
    'Content-Type': 'application/json',
    'X-AurMap-Key-Id': AURMAP_KEY_ID,
    'X-AurMap-Timestamp': ts,
    'X-AurMap-Nonce': nonce,
    'X-AurMap-Signature': sign(method, path, ts, nonce, body),
  };
  if (method !== 'GET') headers['Idempotency-Key'] = idempotencyKey ?? crypto.randomUUID();
  const res = await fetch(AURMAP_HOST + path, { method, headers, body: body || undefined });
  const json = await res.json();
  if (!res.ok) throw Object.assign(new Error(json.error?.code ?? 'HTTP_' + res.status), { status: res.status, error: json.error });
  return json;
}

And in PHP:

php
<?php
function aurmap_sign(string $secret, string $method, string $path, string $ts, string $nonce, string $body): string {
    $canonical = implode("\n", [strtoupper($method), $path, $ts, $nonce, hash('sha256', $body)]);
    return hash_hmac('sha256', $canonical, $secret);
}

function aurmap(string $method, string $path, ?array $payload = null, ?string $idempotencyKey = null): array {
    $body  = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $ts    = (string) time();
    $nonce = bin2hex(random_bytes(16));
    $headers = [
        'Content-Type: application/json',
        'X-AurMap-Key-Id: ' . getenv('AURMAP_KEY_ID'),
        'X-AurMap-Timestamp: ' . $ts,
        'X-AurMap-Nonce: ' . $nonce,
        'X-AurMap-Signature: ' . aurmap_sign(getenv('AURMAP_KEY_SECRET'), $method, $path, $ts, $nonce, $body),
    ];
    if ($method !== 'GET') {
        $headers[] = 'Idempotency-Key: ' . ($idempotencyKey ?? bin2hex(random_bytes(16)));
    }
    $ch = curl_init(getenv('AURMAP_HOST') . $path);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    if ($body !== '') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }
    $raw = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    $json = json_decode((string) $raw, true) ?? [];
    if ($status >= 400) {
        throw new RuntimeException($json['error']['code'] ?? ('HTTP_' . $status), $status);
    }
    return $json;
}

Ask for a link code for a branch in your system:

bash
aurmap POST /v1/link-codes '{"level":"place","external_location_id":"pos-branch-17","external_location_name":"Test branch","scopes":["catalog:write","inventory:write","orders:read","orders:write"]}'
json
{
  "code": "7KQ2M9XA",
  "display_code": "7KQ2-M9XA",
  "qr_url": "https://aurmap.com/link/7KQ2M9XA",
  "status": "pending",
  "expires_at": "2026-10-14T12:15:00Z"
}

In production the merchant scans the code and approves it. In the sandbox you can approve on their behalf, with a button in the dashboard or with this call:

bash
aurmap POST /v1/sandbox/link-codes/7KQ2M9XA/approve '{"place_id":"plc_test_olaya"}'

You receive link.created. Keep its link_id and place_id.

json
{
  "event_id": "evt_01JA9W2C4D",
  "type": "link.created",
  "seq": 1,
  "data": {
    "link": {
      "link_id": "lnk_test_2h6j4n",
      "place": { "place_id": "plc_test_olaya", "name": "Test restaurant - Olaya" },
      "external_location_id": "pos-branch-17",
      "scopes": ["catalog:write", "inventory:write", "orders:read", "orders:write"]
    }
  }
}

4. Publish a menu

Save a small menu as menu.json:

json
{
  "schema": "aurmap.menu/1",
  "currency": "SAR",
  "default_language": "ar",
  "languages": ["ar", "en"],
  "tax": { "prices_include_vat": true, "vat_percent": 15, "service_charge_percent": 0 },
  "aurmap_ordering": { "enabled": true, "pickup": true, "delivery": false, "cash": true },
  "sections": [
    { "id": "burgers", "parent_id": null, "name": { "ar": "البرجر", "en": "Burgers" }, "sort": 0 }
  ],
  "items": [
    {
      "id": "chicken-burger",
      "type": "menu_item",
      "section_id": "burgers",
      "sort": 0,
      "name": { "ar": "برجر دجاج", "en": "Chicken burger" },
      "price_minor": 2300,
      "sizes": [
        { "id": "m", "name": { "ar": "وسط", "en": "Medium" }, "price_minor": 2300 },
        { "id": "l", "name": { "ar": "كبير", "en": "Large" }, "price_minor": 2990 }
      ],
      "group_ids": ["sauce"],
      "order_types": { "pickup": true, "delivery": true, "dine_in": true }
    }
  ],
  "groups": [
    {
      "id": "sauce", "name": { "ar": "الصوص", "en": "Sauce" }, "required": true, "min": 1, "max": 1,
      "options": [
        { "id": "hot", "name": { "ar": "صوص حار", "en": "Hot sauce" }, "price_minor": 230, "image": null },
        { "id": "none", "name": { "ar": "بدون صوص", "en": "No sauce" }, "price_minor": 0, "image": null }
      ]
    }
  ],
  "state": { "paused": false, "sold_out": { "items": [], "sizes": [], "options": [] }, "prices": { "items": [], "sizes": [], "options": [] } }
}

Then publish it:

bash
aurmap PUT /v1/places/plc_test_olaya/menu "$(cat menu.json)"
json
{ "place_id": "plc_test_olaya", "version": 1, "prev_version": 0, "kind": "full", "status": "processing", "hash": "9f2c4be1d07a65e3c1f8a2b4d6e8f0a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3", "warnings": [] }

A few seconds later you receive menu.changed with the same version and hash. The full format is in Catalog. The menu is now visible in the sandbox AurMap app. All amounts are in halalas: 2300 means 23.00 in riyals.

5. Receive a test order

Ask the sandbox to place an order as if a customer had ordered from the app:

bash
aurmap POST /v1/sandbox/orders '{"place_id":"plc_test_olaya","items":[{"item_id":"chicken-burger","size_id":"l","qty":1,"options":[{"group_id":"sauce","option_id":"hot"}]}],"payment":{"method":"card"}}'

Your webhook endpoint receives order.created:

json
{
  "delivery_id": "dlv_test_01JA9Y3K",
  "mode": "test",
  "events": [
    {
      "event_id": "evt_test_01JA9Y3J8Q",
      "type": "order.created",
      "seq": 3,
      "data": {
        "order": {
          "order_id": "ord_test_7m1x9c",
          "number": "M-1001",
          "status": "received",
          "accept_by": "2026-10-14T12:35:05Z",
          "items": [ { "line_id": "1", "item_id": "chicken-burger", "size_id": "l", "qty": 1, "line_total_minor": 3220 } ],
          "totals": { "currency": "SAR", "total_minor": 3220 },
          "payment": { "method": "card", "status": "paid", "amount_minor": 3220 }
        }
      }
    }
  ]
}

Verify the signature first (see Events), then answer 200 within 10 seconds. The answer is not an acceptance. Accept the order before accept_by:

bash
aurmap POST /v1/orders/ord_test_7m1x9c/accept '{"prep_minutes":15,"external_order_id":"POS-88231"}'
aurmap POST /v1/orders/ord_test_7m1x9c/status '{"status":"ready"}'
aurmap POST /v1/orders/ord_test_7m1x9c/status '{"status":"completed"}'
json
{ "order_id": "ord_test_7m1x9c", "status": "completed", "version": 4 }

That is your first successful order. Also try a customer cancellation and an expired accept window from the sandbox.

6. Go live

  1. Complete the certification checklist in the dashboard. Details in Going live.
  2. After certification you get live keys pk_live_xxx and sk_live_xxx.
  3. Switch AURMAP_HOST to the live address and add a live webhook endpoint.
  4. Show a “Connect to AurMap” button to your merchants. Each merchant links their own branches.