Crossdeck Docs
Dashboard

Outbound API — Security & Trust

Security For your security review · Built Stripe-grade

This page documents, point for point, how the Crossdeck Outbound Read API protects the data it serves — so a security owner can evaluate it for a bank-grade system from this page alone. The Outbound API lets your backend pull Crossdeck's data (revenue, errors, read-cost, per-host analytics, and the cross-layer cross-match) into your own product. Like Stripe's API, it is server-to-server, secret-key only, and the security boundary is the key — not a blanket restriction on the data.

Summary for a security review

The key is the boundary

A common question: why would a read API ever return individual data? The answer is the same as Stripe's. Stripe's API returns named customers, emails, and full charge histories — row-level, real-money, PII data — and is the bank-grade reference. It is safe not because the data is hidden, but because the key is the security boundary, backed by key hygiene. Crossdeck follows that model: the data a key can read is governed by the key's scope, and every Stripe-grade defense around the key is stood up (below). The customer owns their data and sees it all on the dashboard; the credential is what we scope and guard.

Two tiers: aggregate & identity

TierWhat it returnsGating
Aggregate (default)Counts, breakdowns, metrics, per-host event segments. Never an individual.Secret key with the relevant read scope. Near-zero compliance weight; embed freely.
Identity (opt-in)The moat at row level: who an error affected and what they pay; a host's leads. Returns your own identifiers + facts.A key minted with the reporting:identity scope — granting it requires acknowledging the row-level-data terms in the same step. The key's scope is the boundary (no separate project switch), enforced on every call.

Restricted keys & scopes

Each secret key carries a per-resource scope map — the Stripe restricted-key model. A key is minted with exactly the scopes it needs:

scopes: {
  reporting:  "aggregate" | "identity" | "none",
  revenue:    "read" | "none",
  errors:     "read" | "none",
  buckets:    "read" | "none",
  crossmatch: "read" | "none"
}

The controls, point for point

Every position below is stood up — none is deferred. The identity tier does not open with any one of them unmanned.

1. Secret-key only, server-to-server

Endpoints accept only cd_sk_… secret keys, on https://api.cross-deck.com. Publishable keys are rejected (401 secret_key_required) — they live in browsers and cannot be trusted to scope a read of private data. Call the API only from your backend.

2. Fail-closed gate

Every outbound read passes through one shared gate that authenticates, resolves the project from the key, validates scope and resource ownership, rate-limits, and records the read — before the endpoint runs. Any error denies; partial data is never served. One gate, not four, so there is one place to reason about and audit.

3. Tenant, host & environment isolation

4. Rate limits & enumeration guard

~2 requests/second sustained, 120 burst, per key, returning 429 with Retry-After. Generous because every call is an O(range) ledger point-read — capped to make scraping and enumeration uneconomical. Every Prism read shares this per-key limit; identity-tier reads carry the additional control of a per-call hash-chained audit (above).

5. Tamper-evident audit

Every outbound read is recorded to a per-project, append-only access log (key id, product, scope, env, decision, timestamp). Integrity is proven two ways:

The chain is verifiable end-to-end (genesis → head) at any time.

6. Leak detection & revocation

Crossdeck participates in GitHub's Secret Scanning Partner Program: GitHub scans public pushes for the cd_sk_ pattern and notifies Crossdeck of a match. A confirmed public-repo leak is a real breach, so the key is revoked immediately — and a loud, multi-channel alert fires so you rotate at once. Revocation here is only ever on a confirmed partner-program match, never a heuristic guess; and you can revoke any key yourself, instantly, from the dashboard.

7. Rotation never breaks a live system

There is no blind-timer auto-rotation. When you rotate a key, the old key keeps working for a 7-day grace window while you roll the new one out — so a hygiene rotation never causes downtime in the wild. A rotated key keeps its scopes verbatim (an identity key stays an identity key). Immediate kill is a separate, deliberate action (or the confirmed-leak case above).

8. PII posture

Error codes

HTTPcodeWhen
401secret_key_required / invalid_api_keymissing, non-secret, or unknown key
403insufficient_scopethe key wasn't minted with the scope the endpoint requires (e.g. an identity endpoint called with an aggregate key)
403origin_not_allowedthe host is not owned by your project
403env_mismatch?env doesn't match the key's environment
429rate_limitedover the rate limit (see Retry-After)

Compliance & data roles

For data your app sends to Crossdeck, you are the data controller and Crossdeck is the processor (UK/EU GDPR, POPIA). The Outbound API returns your own data back to your own backend; the identity tier surfaces row-level data only to you, the controller, behind a scoped key — the same posture as a payment processor returning your customers' records to you. Aggregates carry no individual data. All reads are logged and tamper-evident (above). To erase a user end-to-end, see Delete a user's data.