# Quickstart

From a sandbox key to an agent acting inside a mandate: enroll a live human, authorise an act, issue a mandate with a ceiling, then consume it idempotently.

Every call below is real and answers today. Copy them in order and the last one debits a ceiling.

## 1. Get a sandbox key

Sandbox is the merchant plus the key prefix: `sk_test_`, `pk_test_`. There is no separate host and no environment switch — isolation is by `merchant_id`. See [sandbox](/sandbox).

## 2. Prove a human

From the browser, with a `pk_` key. The account is born with face and voice; no document is asked for here. `user_id` is minted by the engine (the `auth.users` UUID) — a surface never supplies, chooses or guesses one.

POST /functions/v1/process-biovital-enrollment:

```
curl -X POST https://api.sidaxis.com/functions/v1/process-biovital-enrollment \
  -H "x-sidaxis-api-key: pk_test_..." \
  -H "content-type: application/json" \
  -d '{
    "action": "ACTION_GENESIS",
    "merchant_id": "mrc_9f2c",
    "sieve_hash": "b41d...",
    "consent": { "version": "2026-06", "locale": "en", "accepted_at": "2026-09-16T12:00:03Z" },
    "device_signature": "ed25519:...",
    "client_instance_id": "ci_3f8a",
    "device_label": "iPhone 15",
    "face_template": { "vector": [0.0121, -0.4412, "... 128 numbers total"], "quality": 0.93 },
    "biometric_vitals": {
      "bpm": 74, "hrv": 41, "stress_score": 0.18,
      "voice_pulse_correlation": 0.86,
      "liveness_confidence": 0.97, "liveness_proof": "rppg:..."
    }
  }'
```

## 3. Authorise an act

From your server, with an `sk_` key. `amount` is an integer in the minor unit — `4000` is forty dollars. The row this call writes is the capture a mandate accepts as proof, and it is valid for five minutes.

POST /functions/v1/process-biovital-transaction:

```
curl -X POST https://api.sidaxis.com/functions/v1/process-biovital-transaction \
  -H "x-sidaxis-api-key: sk_test_..." \
  -H "x-sidaxis-timestamp: 1789564803" \
  -H "x-sidaxis-nonce: 9f2c41ab7d" \
  -H "content-type: application/json" \
  -d '{
    "action": "ACTION_PAYMENT",
    "merchant_id": "mrc_9f2c",
    "sieve_hash": "b41d...",
    "user_id": "0f9c2a51-7c33-4a1e-9b02-16c2b7f0d5aa",
    "amount": 4000,
    "currency": "USD",
    "face_template": { "vector": ["... 128 numbers"], "quality": 0.94 },
    "biometric_vitals": { "bpm": 71, "voice_pulse_correlation": 0.88, "liveness_confidence": 0.96 }
  }'
```

200:

```
{
  "success": true,
  "capture_id": "cap_7f31c9a2",
  "assurance_level": "face_voice",
  "receipt_code": "RCP-4K2M-9QX7",
  "sieve_hash": "b41d..."
}
```

## 4. Issue a mandate

`proof.capture_id` comes from step 3 and must be no older than five minutes. `assurance_level` is read from the level frozen on that capture and is never sent by the client.

POST /functions/v1/mandate-issue:

```
curl -X POST https://api.sidaxis.com/functions/v1/mandate-issue \
  -H "x-sidaxis-api-key: sk_test_..." \
  -H "x-sidaxis-timestamp: 1789564821" \
  -H "x-sidaxis-nonce: 41c8de92aa" \
  -H "content-type: application/json" \
  -d '{
    "granted_by": "0f9c2a51-7c33-4a1e-9b02-16c2b7f0d5aa",
    "granted_to": "agt_3f77c1de",
    "granted_to_kind": "agent",
    "agent_display_name": "Your travel assistant",
    "agent_descriptor": "Books flights and hotels inside the limit you set",
    "scopes": ["payment"],
    "constraints": { "merchant_allowlist": ["mrc_9f2c"], "country_allowlist": ["US"] },
    "ceiling": { "kind": "amount", "amount": 50000, "currency": "USD", "period": "daily" },
    "expires_at": "2026-12-31T23:59:59Z",
    "proof": { "capture_id": "cap_7f31c9a2" }
  }'
```

## 5. Let the agent act

`mandate-consume` is the only call that moves the ceiling, and `Idempotency-Key` is mandatory: without it a network timeout becomes a double charge.

POST /functions/v1/mandate-consume:

```
curl -X POST https://api.sidaxis.com/functions/v1/mandate-consume \
  -H "x-sidaxis-api-key: sk_test_..." \
  -H "Idempotency-Key: booking-88213-attempt-1" \
  -H "x-sidaxis-timestamp: 1789564902" \
  -H "x-sidaxis-nonce: 7de1a9c034" \
  -H "content-type: application/json" \
  -d '{
    "mandate_id": "mdt_a91f3c",
    "scope": "payment",
    "amount": 18900,
    "currency": "USD",
    "action_ref": "booking-88213",
    "context": { "merchant": "mrc_9f2c", "category": "travel", "country": "US" }
  }'
```

200 — refused, and still a 200:

```
{
  "allowed": false,
  "reason": "ceiling_exceeded",
  "developer_message": "Requested 18900 exceeds the remaining daily ceiling of 12000.",
  "user_message": "This is above the daily limit you set.",
  "step_up_available": true,
  "remaining": 12000,
  "period_resets_at": "2026-09-17T03:00:00Z"
}
```

Branch on `reason`. A refusal is the layer working, not an error to catch. `mandate-check` is not part of this path: it is the advisory “could I plan this?” call and it authorises nothing — see [mandates/check](/mandates/check).

## 6. Read the receipt

POST https://api.sidaxis.com/rest/v1/rpc/get_receipt_v2:

```
curl -X POST https://api.sidaxis.com/rest/v1/rpc/get_receipt_v2 \
  -H "apikey: <publishable anon key>" \
  -H "content-type: application/json" \
  -d '{ "lookup_key": "RCP-4K2M-9QX7" }'
```

The response carries the assurance block — `face`, `face_voice` or `face_voice_doc` — and, when a mandate authorised the act, the agent's name. See [receipts/verify](/receipts/verify) and [assurance](/assurance).

## The replay headers

`x-sidaxis-timestamp` (unix seconds, accepted within ±300 s) and `x-sidaxis-nonce` (at least 8 characters, single use) are required on `face-identify`, `process-biovital-transaction` and every `mandate-*` call. They make a captured request useless to replay.
