API keys

Every Qariyb integration is issued a matched pair of keys from a single organization: a publishable key for the browser and a secret key for your server.

  • Publishable key (pk_live_…) authenticates the public widget API (/api/v1/public/*) — safe to embed in the browser, like Stripe's publishable keys. Sent in the X-Widget-Key header.
  • Secret key (sk_live_…) authenticates the REST API (/api/v1/*) — server-side only, keep it private. Sent in the Authorization: Bearer header (or X-Secret-Key).

Both keys are scoped to a single organization and can only access that organization's events, checkout, and RSVP data.


Key format

Each integration has one publishable key and one secret key, created together. Keys are always live. Test mode was removed: there are no pk_test_ / sk_test_ variants, and a key using those prefixes is rejected on format.

KeyFormatWhere it goes
Publishablepk_live_ + 32 random chars (40 in total)X-Widget-Key header on public widget requests
Secretsk_live_ + 48 random chars (56 in total)Authorization: Bearer header on REST API requests

The publishable key travels in the X-Widget-Key header on every public request:

curl https://api.qariyb.com/api/v1/public/events/01HWX9KPQR \
  -H "X-Widget-Key: pk_live_..." \
  -H "Accept: application/json"

The secret key travels in the Authorization header on server-to-server REST API requests:

curl https://api.qariyb.com/api/v1/events \
  -H "Authorization: Bearer sk_live_..." \
  -H "Accept: application/json"

The secret key is shown once at creation and never again — Qariyb stores only a SHA-256 hash. If lost, rotate the key to issue a new secret. The publishable key can be revealed again at any time.


Managing keys

Keys live under Developer → API keys in the organizer dashboard. Each key carries:

  • Name
    name
    Type
    string
    Description

    Human-readable label shown in the dashboard.

  • Name
    description
    Type
    string?
    Description

    Optional free-text description.

  • Name
    is_active
    Type
    boolean
    Description

    When false, every request using the key returns 602 API_KEY_INACTIVE. Toggle from the dashboard to disable a key without deleting it.

  • Name
    restrict_domains
    Type
    boolean
    Description

    When true, publishable-key requests are rejected unless the Origin (or Referer) host matches allowed_domains. When false, no origin check runs.

  • Name
    allowed_domains
    Type
    string[] | null
    Description

    List of allowed hostnames. Wildcards supported: *.example.com matches app.example.com and example.com. Hostnames are case-insensitive and the request port is stripped before matching.

  • Name
    rate_limit_per_minute
    Type
    integer
    Description

    Per-key read rate limit (default 120). The checkout/RSVP write limit is exactly half this value, rounded — 60/min at the default.

  • Name
    expires_at
    Type
    datetime?
    Description

    Optional expiration. When in the past, the key returns 603 API_KEY_EXPIRED.

  • Name
    last_used_at
    Type
    datetime?
    Description

    Updated by the ValidateApiKey / ValidateSecretKey middleware on every successful request (recorded after the response is sent, so it never adds request latency).

  • Name
    total_requests
    Type
    integer
    Description

    Incremented on every successful request.

  • Name
    checkout_count
    Type
    integer
    Description

    Incremented only on /checkout/* requests.


Domain restrictions

Domain restrictions apply to the publishable key, since it is the key exposed in the browser. When restrict_domains is enabled, the request must include either an Origin or Referer header whose host matches an entry in allowed_domains. Examples:

PatternMatchesDoes not match
example.comexample.comapp.example.com, evil.com
*.example.comapp.example.com, staging.example.com, example.comevil-example.com
localhostlocalhostlocalhost.evil.com

A failing match returns 604 API_KEY_ORIGIN_DENIED with HTTP 403.


Rotation

Rotate a key when:

  • The secret may have been exposed.
  • A team member with access leaves.
  • An embedding partner is removed.

Rotation invalidates the previous secret immediately and issues a new one. The publishable key remains the same, so existing widgets keep working — only the server-side secret needs to be updated.


Errors

See the error reference for the full code table. Common failures on the public widget API:

CodeHTTPCause
600 API_KEY_MISSING401No X-Widget-Key header or widget_key query parameter
601 API_KEY_INVALID401Key not found, or not in pk_live_… format
602 API_KEY_INACTIVE401is_active = false
603 API_KEY_EXPIRED401expires_at in the past
604 API_KEY_ORIGIN_DENIED403Origin not in allowed_domains
605 API_KEY_RATE_LIMITED429Exceeded rate_limit_per_minute (or its checkout sub-limit)

The secret key used by the server-to-server REST API reports its own failures as 606 SECRET_KEY_MISSING and 607 SECRET_KEY_INVALID. Unlike the publishable key, it does not distinguish unknown from revoked from expired — that distinction would let the endpoint be used to probe which secret keys exist.

Was this page helpful?