Skip to content

Authentication

Every /api/v1 request authenticates with a secret API key, presented as a Bearer token:

Terminal window
curl https://app.personablocks.io/api/v1/verification-sessions \
-H "Authorization: Bearer pb_sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "flows": ["wallet", "phone"], "test_outcome": "approved" }'

There is no cookie, session, or query-parameter fallback — the Authorization: Bearer header is the only way in. A request without a valid key never reaches a resource handler.

Keys come in two modes, distinguished by prefix:

PrefixModeBehavior
pb_sk_test_Test (sandbox)Never billed, never written on-chain, never sends SMS. Reads the sandbox, not live customer data.
pb_sk_live_Live (production)Real verifications, real billing, real on-chain writes.

The mode is a property of the key, never of the request body. You never pass a livemode flag — the key you present decides the mode, and the response echoes it back as livemode: true|false. This is deliberate: it makes it impossible to accidentally run test traffic against production, or vice versa, by fumbling a request field.

Keys are minted by the merchant account owner in the portal (Account → Developers). Each key:

  • is scoped — it carries a fixed set of permissions chosen at mint time (see below);
  • is stored only as a SHA-256 hash — PersonaBlocks never keeps the plaintext;
  • is shown exactly once, on the create response. Copy it into your secret store immediately. If you lose it, you cannot retrieve it — revoke it and mint a new one.

A key grants access only to the scopes it was minted with. A request to a route whose required scope is absent fails with 403 missing_scope. The seven scopes are:

ScopeGrants
sessions:writeCreate, read, list, and cancel verification sessions.
customers:readRead and list customers and their notes.
customers:writeSuspend/unsuspend customers and write notes.
decisioning:readRead reviews and decision rules.
decisioning:writeResolve reviews.
reverification:writeMint and read reverification requests.
webhooks:manageManage webhook endpoints and read the event log.

Grant a key the narrowest set it needs. A backend that only mints sessions and consumes webhooks needs sessions:write and webhooks:manage and nothing more.

The /api/v1 surface accepts secret keys only, and secret keys work only here. Two boundaries are enforced in both directions:

  • Secret keys are rejected by the portal. A pb_sk_... key sent to a /api/merchant/* portal route is refused — those routes authenticate a browser session, not an API key.
  • WooCommerce Store Keys are rejected by /api/v1. A pbk_... Store Key belongs to a different program (it licenses the WooCommerce plugin and authenticates that plugin’s own endpoints). It is not an API key, and /api/v1 refuses it.

These are two disjoint credential spaces on purpose — a Store Key licenses the plugin, a secret key calls the public API.

There is no in-place rotation. To rotate, mint a new key, deploy it, then revoke the old one from the Developers tab. Overlapping the two briefly lets you cut over with zero downtime before you retire the previous key.

  • Errorsmissing_api_key, invalid_api_key, missing_scope, live_key_required.
  • Sandbox & testing — what a pb_sk_test_ key can and can’t do.
  • Go live — the checklist gating your first pb_sk_live_ key.
  • Web SDK — how the frontend authenticates with a pbvs_ client token, never a secret key.