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

Developers

Keys and signing

How to sign every AurMap Partner API request with HMAC-SHA256: keys, headers, clock skew, nonces, retries and rotating keys without downtime.

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

Keys

Each partner account has separate keys for the sandbox and for live. You create and revoke them in the developer dashboard at developers.aurages.com Coming soon.

Item Format Use
Key id pk_test_xxx / pk_live_xxx sent with every request; not a secret
Key secret sk_test_xxx / sk_live_xxx signs requests; shown once at creation
Webhook secret whsec_xxx verifies the events we send you
  • A test key works only on the sandbox address, a live key only on the live address.
  • Live keys are issued after certification.
  • The secret belongs on your server only. Never in a mobile app, a browser or a log.
Environment Address
Sandbox https://sandbox.partners.aurmap.com/v1 (live now)
Live https://partners.aurmap.com/v1 (opens for each developer once approved)

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

Headers

Every request carries:

Header Value
X-AurMap-Key-Id the key id
X-AurMap-Timestamp current unix time in seconds
X-AurMap-Nonce 16–128 chars of [A-Za-z0-9_-], new for every request
X-AurMap-Signature the signature, lowercase hex
Idempotency-Key required on every POST, PUT, PATCH and DELETE. See Errors

How to sign

Build a five-line string, separated by \n:

METHOD
PATH
TIMESTAMP
NONCE
hex(SHA256(raw_body))

Then compute HMAC-SHA256 of that string with your secret, as hex.

  • METHOD in upper case: GET, POST, PUT, PATCH or DELETE.
  • PATH is everything after the host exactly as you send it, including /v1 and the query string. Example: /v1/orders?status=received&limit=50.
  • raw_body is the exact bytes you send. Sign the string you send, not an object you re-encode after signing.
  • A request with no body signs the empty string, whose hash is e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.

Test vector

Secret sk_test_example_secret_do_not_use
Method PATCH
Path /v1/places/plc_9q3w7e/menu/state
Timestamp 1760443200
Nonce n0nce_Example_1234567890
Body {"as_of":"2026-10-14T18:00:00Z","sold_out":{"items":["kunafa"],"sizes":[],"options":[]}}
Body hash 087c75de7bec170df0facfe62fba13182441513acb4f7fc4ad1289eca35d4971
Signature ec96466e5cc1ab8e845d984b3cec264483e38357fb849a3c81e4e26f74abb3c8

If your code produces the same signature, it is correct.

Node

js
import crypto from 'node:crypto';

export function sign(secret, 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', secret).update(canonical).digest('hex');
}

// the test vector above
console.log(sign(
  'sk_test_example_secret_do_not_use', 'PATCH',
  '/v1/places/plc_9q3w7e/menu/state', '1760443200',
  'n0nce_Example_1234567890', '{"as_of":"2026-10-14T18:00:00Z","sold_out":{"items":["kunafa"],"sizes":[],"options":[]}}'
)); // ec96466e...b3c8

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);
}

echo aurmap_sign(
    'sk_test_example_secret_do_not_use', 'PATCH',
    '/v1/places/plc_9q3w7e/menu/state', '1760443200',
    'n0nce_Example_1234567890', '{"as_of":"2026-10-14T18:00:00Z","sold_out":{"items":["kunafa"],"sizes":[],"options":[]}}'
); // ec96466e...b3c8

curl

bash
BODY='{"as_of":"2026-10-14T18:00:00Z","sold_out":{"items":["kunafa"],"sizes":[],"options":[]}}'
PATH_Q='/v1/places/plc_9q3w7e/menu/state'
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' PATCH "$PATH_Q" "$TS" "$NONCE" "$BH" \
      | openssl dgst -sha256 -hmac "$AURMAP_KEY_SECRET" -hex | sed 's/^.* //')

curl -sS -X PATCH "$AURMAP_HOST$PATH_Q" \
  -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: state-kunafa-0001" \
  --data-raw "$BODY"

The quickstart has a ready aurmap function that does all of this; the other pages use it.

Clock skew

  • We accept a request when X-AurMap-Timestamp is within 300 seconds of our time, either way.
  • Outside that we answer 401 TIMESTAMP_SKEW.
  • Every response carries a Date header. Compare it with your clock if the error repeats, and run NTP on your servers.

Nonces

  • Each nonce is accepted once within 10 minutes per key. Reuse answers 401 NONCE_REUSED.
  • A badly signed request does not spend the nonce, so nobody can burn yours.
  • If we answer 503 UNAVAILABLE, retry with a new nonce.

Retries

A retry is a new request: new timestamp, new nonce, new signature. But it carries the same Idempotency-Key. That way a command never runs twice.

Rotating keys

  1. Create a new key in the dashboard. Both keys work at the same time.
  2. Deploy the new key to your servers.
  3. Watch “last used” for the old key in the dashboard. When it stops, revoke it.
  4. Revocation is immediate. If you think a secret leaked, revoke it at once and create another.

Idempotency keys and rate limits belong to your account and environment, not to the key. Rotating never re-runs a command or changes your limits.

Webhook secrets rotate the same way. During the overlap we send two signatures in the same header; accept the message if either matches.

Signing errors

Code Cause
KEY_UNKNOWN the key id does not exist
KEY_REVOKED the key was revoked
KEY_MODE_MISMATCH a test key on the live address, or the reverse
TIMESTAMP_SKEW the time is more than 300 seconds from ours
NONCE_INVALID the nonce has the wrong length or characters
NONCE_REUSED the nonce was used before
SIGNATURE_INVALID the signature does not match

All answer 401. The most common cause of SIGNATURE_INVALID: signing the path without its query string, or re-encoding the JSON after signing.