Ontwikkelaars · Voorvertoning

Quickstart voor de Customer API

Volg de onderstaande stappen om een token te minten, trades te lezen en een niet-monetaire aanbieding te maken.

# Customer API quickstart

Use the Customer API to read your organisation's settlement data and create
non-money offers. Create an API client in **Settings → API clients** first.
Loam shows its secret once; store it in your secret manager, not in source code.

Prerequisites: `curl`, `jq`, and `uuidgen` (or another UUID generator). Set
`LOAM_API_URL` to your operator host (for example a leased Studio stack or a
registered non-production host). Vercel Preview hosts reject token minting.

```sh
export LOAM_API_URL="https://your-loam-host"
export LOAM_CLIENT_ID="client_..."
export LOAM_CLIENT_SECRET="secret_..."
```

## Get an access token

The token endpoint uses OAuth 2.0 client credentials and requires a
form-encoded body. Token errors follow RFC 6749
(`{ "error": "...", "error_description": "..." }`), not the Customer API
envelope used by `/api/v1/*` resource routes.

```sh
ACCESS_TOKEN="$(
  curl --fail-with-body --silent --show-error \
    --request POST "$LOAM_API_URL/api/v1/oauth/token" \
    --header "Content-Type: application/x-www-form-urlencoded" \
    --data-urlencode "grant_type=client_credentials" \
    --data-urlencode "client_id=$LOAM_CLIENT_ID" \
    --data-urlencode "client_secret=$LOAM_CLIENT_SECRET" \
  | jq --raw-output ".access_token"
)"
```

The response has `access_token`, `token_type`, `expires_in`, and the granted
`scope`. Tokens are short lived; mint a new one instead of persisting it.

## Make a first request

Use the token as a bearer credential. Resource routes use an envelope:
`{ "ok": true, "data": ... }` for success and
`{ "ok": false, "error": ... }` for errors.

```sh
curl --fail-with-body --silent --show-error \
  "$LOAM_API_URL/api/v1/trades?limit=20" \
  --header "Authorization: Bearer $ACCESS_TOKEN" | jq
```

## Create an offer safely

Create a UUID for each logical write and reuse it when retrying the same
request. The response is `202 Accepted` and includes the offer identifier.

Replace `LISTING_ID` with a published listing from your operator host before
running the write.

```sh
IDEMPOTENCY_KEY="$(uuidgen | tr '[:upper:]' '[:lower:]')"
LISTING_ID="replace-with-a-published-listing-uuid"

curl --fail-with-body --silent --show-error \
  --request POST "$LOAM_API_URL/api/v1/offers" \
  --header "Authorization: Bearer $ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data "{
    \"listing_id\": \"$LISTING_ID\",
    \"offered_price\": { \"minor_units\": 2500000, \"currency\": \"USD\" }
  }" | jq
```

If the network fails after sending a write, retry with the same
`Idempotency-Key` and the identical request body. A new key represents a new
operation.

## Money movement

Payment and funding endpoints are deliberately more restrictive. A human
organisation owner must grant `money.write` under fresh AAL2, define limits,
and approve the destination before the machine can submit a payment. A `403`
`forbidden` means the destination is visible but authorization refused; an
unknown or invisible `destination_id` returns `400` `invalid_input`. Do not
blind-retry either as a transient failure.

## Errors and rate limits

| Status | Meaning                                        | Client action                                                               |
| ------ | ---------------------------------------------- | --------------------------------------------------------------------------- |
| `400`  | `invalid_input`                                | Correct the request and submit a new logical operation.                     |
| `401`  | Invalid, expired, or revoked credentials       | Mint a new token; rotate/revoke only when the client secret is compromised. |
| `403`  | `forbidden` or `destination_revoked`           | Change the human-controlled grant or allowlist; do not blind-retry.         |
| `409`  | `conflict`                                     | Fetch the resource and resolve the concurrent state before retrying.        |
| `429`  | `rate_limited`                                 | Wait for the `Retry-After` duration, then retry.                            |
| `503`  | `transient_error` or `temporarily_unavailable` | Retry reads with backoff; retry writes only with the same idempotency key.  |

See the interactive API reference at `$LOAM_API_URL/developers/reference` for
all endpoints, schemas, scopes, and response details.