Public API v1

Create a code

Idempotency-Key is REQUIRED, not optional. Creating a code consumes plan quota and mints a permanent slug, so a client that times out and retries without one ends up with two codes and no way to tell which it printed. Requiring the header turns that from a silent accounting problem into a 400 on your very first call.

A retry with the same key replays the stored response verbatim, with Idempotent-Replay: true. The same key with a different body is a 409 rather than a silent no-op.

Plan quota, destination screening and reserved-slug refusal are enforced here exactly as they are in the dashboard. There is no path through this API that bypasses an abuse control.

post/api/v1/codes

Request

  • Base URL: https://scanduro.com
  • URL: https://scanduro.com/api/v1/codes
  • Auth: HTTP bearer

Headers

Idempotency-Keystring required

Any unique string per logical creation, e.g. a UUID. Completed responses are retained for 24 hours. Concurrent requests are claimed before creation; an in-flight or ambiguous request returns 409 request_in_progress with Retry-After. Reuse the same key. Ambiguous unfinished claims are retained for operator reconciliation, never auto-reissued.

Request body

namestring required
type'static' | 'dynamic' required
kind'qr' | 'link'
contentType'url' | 'text' | 'vcard' | 'wifi' | 'email' | 'sms' | 'geo' | 'pdf' | 'gs1' required
slugstring

Ignored for GS1: the server derives a deterministic canonical resolver key from payload.

destinationstring required

For GS1, the screened http(s) redirect destination.

payloadstring nullable

For contentType gs1, JSON text matching Gs1Payload. GS1 codes must be dynamic and use an auto-derived canonical key.

designstring required
scanCapinteger nullable
customDomainstring nullable
subdomainstring nullable

A claimed layer-2 LABEL ("sisar"), never a host. The server derives the host from the claim table, so a caller cannot name a host it does not own. 501 when the deployment cannot serve branded subdomains, 402 when the plan does not include one, 404 when this workspace has not claimed the label, and 400 on a sandbox key because a test row does not resolve on any host. Omit it and the code is created on the shared resolver.

domainstring nullable

A layer-3 host this workspace has already added AND verified (see POST /api/domains). Unlike subdomain this is a full hostname, because there is no apex to derive it from - but it is checked against this workspace's own verified list, so supplying a host still proves nothing. Takes precedence over subdomain when both are given. 402 when the plan does not include custom domains, 400 on a sandbox key, and a single 404 domain_unknown for not yours / never added / not verified / abandoned alike, so the endpoint cannot be used as an oracle for which hostnames another workspace has registered.

Example request

{
  "subdomain": "sisar",
  "domain": "go.example.com"
}

Response

Created, or replayed from a previous identical request.

Example response

{
  "data": {
    "id": "lnk_4f2c19ab7d0e46f1a8b3c5d7e9f10203",
    "hostname": "sisar.scandu.ro"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.