SharpRelayPinnacle data
HomeAPI referenceDashboard

Authentication

Every API request is authenticated with an API key sent as an HTTP Bearer token. Each key has an explicit, non-empty scope set, while quota and rate limits remain shared by the account.

Sending your key

$ curl -H "Authorization: Bearer sr_live_…" https://api.worksinprod.dev/v1/catalog/sports

Buyer credentials are accepted only through the standard Authorization: Bearer scheme. Never place a key in a URL, query parameter, cookie, or custom header.

Key anatomy

PropertyValue
Formatsr_live_ + 32 random characters (192 bits of entropy)
IssuedThe primary key is delivered through a private one-time claim after confirmed admission; timing depends on access verification and service availability. Additional keys can be created from the dashboard while account and key limits allow.
Storage (our side)Only a SHA-256 hash is used for key lookups. Server-decryptable ciphertext exists only until an initial claim is first delivered or expires. Interrupted claim, rotation, and account-recovery deliveries use short-lived caller-sealed bytes that cannot be opened without the exact client-held bearer or credential pair. Recovery-email verification is a separate hash-only capability and never contains an API key.
LimitUp to 5 active keys per account

Least-privilege scopes

ScopeAllows
data:readCalls to the Pinnacle-sourced data endpoints under /v1.
account:manageAccount details, usage, key inventory, creation, revocation, rotation, recovery-email verification resend, and hosted billing management.

Initial primary keys and support-issued keys receive both scopes. Additional buyer keys default to data:read, which is safest for production workers because a leaked worker key cannot inspect the account, mint broader credentials, revoke another key, or rotate itself. An account-managing key may explicitly request any valid non-empty subset:

$ curl -X POST https://api.worksinprod.dev/v1/account/keys \
    -H "Authorization: Bearer sr_live_…" \
    -H "Content-Type: application/json" \
    -d '{"name":"production poller","scopes":["data:read"]}'

Keeping keys safe

Rotating and revoking keys

Rotation requires account:manage, preserves the calling key's exact scopes, and immediately revokes the old key. There is no overlap window, so rotate when you can deploy the new key promptly. Every rotation also requires a fresh idempotency nonce: rot_ followed by the canonical unpadded base64url encoding of exactly 32 cryptographically random bytes.

$ curl -X POST https://api.worksinprod.dev/v1/account/keys/rotate \
    -H "Authorization: Bearer sr_live_OLD…" \
    -H "Idempotency-Key: rot_BASE64URL_OF_32_RANDOM_BYTES"

# → { "api_key": "sr_live_NEW…", "scopes": ["data:read", "account:manage"],
#     "recovery_expires_at": "…", "message": "…" }
Do not lose an interrupted rotation

Keep the old key and nonce in protected memory until the new key is stored. If the response is interrupted, retry with that exact old key and exact nonce before recovery_expires_at (15 minutes). The old key stays revoked; this pair can recover only the already-created replacement. Never generate a new nonce for an ambiguous attempt.

You can also create extra keys (POST /v1/account/keys) and revoke individual keys (POST /v1/account/keys/{id}/revoke) — all available in the dashboard with one click, no curl needed.

Verify recovery before you need it

Your account email address has no lost-key recovery authority until someone with access to that mailbox explicitly verifies it. This does not delay normal API use and it does not reveal, create, revoke, replace, or email an API key. Check the current state in the dashboard or in GET /v1/account:

{
  "recovery_email": {
    "verified": false,
    "verified_at": null
  }
}

New buyers receive a separate verification message after provisioning. While you still have an account:manage key, you can request another link from the dashboard or with an exactly empty JSON object:

$ curl -X POST https://api.worksinprod.dev/v1/account/recovery-email/verification/resend \
    -H "Authorization: Bearer sr_live_…" \
    -H "Content-Type: application/json" \
    -d '{}'

The endpoint is durably limited to one request per customer every 10 minutes. It returns the same generic 202 when verification is already complete or a resend is cooling down, so acceptance does not prove that a new message was sent.

Verification grants recovery authority

The single-use link expires after 24 hours and keeps its emv_… bearer in the /verify-recovery-email URL fragment. The first-party page removes that fragment before any request and asks for explicit confirmation. Only verify a mailbox controlled by the buyer. If you received the message but are not the buyer, ignore it; never forward the link or paste it into support, chat, logs, or analytics. Verification advances a separate recovery-authority generation. Every later recovery link is bound to that exact generation and account address; replacing or resetting the address permanently invalidates older links, and recovery stays disabled until the new mailbox is verified separately.

Lost every management key?

If no active key with account:manage remains, open the secure account-recovery page. After a hostname-bound Turnstile challenge, the page always gives the same accepted response; it does not reveal whether an address has an account or is verified. The synchronous request is placed in a fixed-size encrypted blind queue without an account lookup and is resolved asynchronously; allow a few seconds for delivery. A request for an unverified mailbox issues nothing. Only an eligible account whose exact current account mailbox was verified beforehand can receive a 30-minute same-origin fragment link by email. Delivery, claim, and interrupted-response retry all fail closed if the verified address or its recovery-authority generation has changed since that link was issued.

Claiming that link revokes every existing API key and creates exactly one replacement with both scopes. If delivery is interrupted, the exact same link bearer can return that replacement for 10 minutes. Paid accounts must be active or past_due inside their grace window; private-beta access must not have reached its fixed expiry. Suspended, canceled, expired, held, unverified, or reconciliation-required accounts must contact support. The replacement appears only on the first-party page and is never emailed. Never paste a recovery link or bearer into a support ticket.

Managing billing

Paid Stripe-backed accounts can update payment details, view invoices, or use the configured cancellation flow from a fresh hosted billing session. Open it from the dashboard, or call the authenticated endpoint below. The API chooses the Stripe customer and return address from your account; it accepts no redirect input.

$ curl -X POST https://api.worksinprod.dev/v1/account/billing-portal \
    -H "Authorization: Bearer sr_live_…"

# → { "portal_url": "https://billing.stripe.com/p/session/…" }

The URL is short-lived. Open it immediately and never log, cache, bookmark, or share it. This endpoint is not registered during private beta. Contact support for plan changes.

When authentication fails

StatusCodeMeaning
401invalid_api_keyMissing, malformed, or revoked key. Check the header and that the key is still active.
403insufficient_scopeThe key is valid but lacks the capability required by this endpoint. Use a correctly scoped key; do not broaden worker keys unnecessarily.
403account_suspendedYour account was suspended, expired, or the subscription ended. Contact support for the applicable access review.
Tip

Every error response includes a request_id. Quote it when contacting support and we can trace the exact request. See Errors & retries.