The key is bound to your organization. There is no org id parameter anywhere
in the API - your key is the tenancy. Isolation is enforced with
row-level security at the database layer, so a request can only ever read or
write rows belonging to your organization.
Treat org_ keys like passwords: server-side only, never in client bundles,
rotate if exposed. Issue one from an authenticated partner admin session as described in the Quickstart.
An org_ key authenticates a service identity scoped to one organization. It cannot publish a module, install a module, issue a module credential, or mint another org_ key. Use a partner admin session for those operations. When setting up a private module, author and publish it with the admin session, then mint the org_ key afterward for any service that needs partner reads.
Key management, tracking-key management and module authoring need a partner admin session rather than an org_ key. A session token comes from signing in, either in the console or over the email OTP endpoints:
POST /v1/auth/email-otp/request with {"email": "<admin email>"} sends a one-time code to that address.
POST /v1/auth/email-otp/verify with {"email": "<admin email>", "otp": "<code>"} returns a token, the user, and enrollmentRequired.
If enrollmentRequired is true, the administrator has no authenticator yet. POST /api/totp/enroll with the token as a bearer returns secret (base32), otpauthUri (for a QR code) and backupCodes. Add the secret to an authenticator app and keep the backup codes; they are shown once. GET /api/totp/status with the same bearer answers {"enrolled": <bool>, "enrollmentRequired": <bool>} at any point.
Administrators must complete the second factor before any admin operation: POST /api/auth/two-factor/verify-totp with {"code": "<authenticator code>"} and the token as a bearer. Until then, admin routes answer 401 with the reason 2FA_REQUIRED.
POST /v1/session/active-organization with {"organizationId": "<organization id>"} and the token as a bearer makes that organization the session’s active one. Sign-in picks the admin’s earliest seat on its own, so an administrator with seats on more than one organization does this before minting a key or publishing a module, or the key or module lands in the other organization.
The first verify-totp after enrolment replaces the session. The token from step 2 stops working, and the new session token arrives in the set-auth-token response header. The token in that response’s body is the old one, so do not use it. Use the header value for step 5 and every call after it. Later sign-ins, with the authenticator already enrolled, keep the step 2 token and send no set-auth-token header.
curl -i -X POST "$API_BASE_URL/api/auth/two-factor/verify-totp" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"code":"123456"}'# On first enrolment, read the new token from the response header:# set-auth-token: <new session token>
Send the token as Authorization: Bearer <token> on the key and module endpoints. A session belongs to the admin who signed in and acts on the session’s active organization; a key response carries that organization as organization_id and a module response as owner_organization_id, so check the one you meant. The token is not a credential to store or share.
export ADMIN_TOKEN="<session token from the sign-in steps above>"curl "$API_BASE_URL/v1/api-keys" \ -H "Authorization: Bearer $ADMIN_TOKEN"
Browser event tracking and publisher ad slots can’t hide a secret, so they
don’t use one. POST /v1/track, POST /v1/content-interactions and
GET /v1/ads/serve (publisher ads) authenticate with a
publishableek_live_… key that ships in client code, like an analytics
measurement id. Abuse is bounded by two controls on the key itself, not by
secrecy:
a per-key origin allowlist - tracking and ad requests are only accepted
from browser origins you configured for the key;
per-key rate limiting.
You issue, list and revoke tracking keys with a partner admin session token
(Admin session tokens), not with an org_ key: an
org_ key gets 403 FORBIDDEN on /v1/engagement/keys. The console has no
tracking-key screen yet, so keys are managed over the API only. See
Trigger an event.
A publishable key identifies your site, not the fan reading it, so
GET /v1/ads/serve never takes a member id from the page. To target a
signed-in fan, your server mints a signed identity with its organization API
key and hands it to the page:
Name the fan by your own member_external_id or by the platform user_id,
exactly one. The answer is 404 when no active member of your organization
matches. The token is opaque, bound to your organization and that member, and
expires after one hour, so mint a new one before then. The page passes it as
identity_token on serve. A missing, expired, altered or other-club token
serves the slot as if the fan were anonymous rather than failing, and so do
the unsigned member_external_id and user_id serve parameters, which no
longer identify anyone.Impressions and clicks are recorded against the member only when the fan
holds your club’s personalised content consent; without it they are counted
anonymously. Sponsors never see which fan saw or clicked an ad.
Reading a club’s published articles from its own website has the same problem
and the same answer: a publishable ck_live_… key in the page, with a per-key
origin allowlist as the control. You issue content keys in the console rather
than over the API. See Content API authentication.
Installed modules push events server-to-server via POST /v1/ingest with an
mk_live_... bearer credential plus an HMAC signature header (x-4d-signature) and an Idempotency-Key. Issue this credential from an admin session after installing a private module. See Publish a private module.
Missing or invalid credentials are a 401; a valid credential without
permission for the operation is a 403 or, where the resource’s existence
shouldn’t be revealed, a 404. code values are the stable contract to
program against.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"4D OS Developer Docs","description":"Public developer documentation for the 4D OS partner platform","colors":{"primary":"#1865D1","light":"#80AAE6","dark":"#1353AE"},"logo":{"light":"https://mintcdn.com/4dos/zox555bidSKp__2J/logo/light.svg?fit=max\u0026auto=format\u0026n=zox555bidSKp__2J\u0026q=85\u0026s=f664509c181b052b368f5e6d97c443cb","dark":"https://mintcdn.com/4dos/zox555bidSKp__2J/logo/dark.svg?fit=max\u0026auto=format\u0026n=zox555bidSKp__2J\u0026q=85\u0026s=ae86b94c4df9a59c79ef008d269dd134"},"favicon":"https://mintcdn.com/4dos/zox555bidSKp__2J/favicon.svg?fit=max\u0026auto=format\u0026n=zox555bidSKp__2J\u0026q=85\u0026s=5e2a089a22e686c5e5c26a6abd8717e4","background":{"color":{"light":"#FFFFFF","dark":"#15161E"}},"footer":{"socials":{}},"fonts":{"heading":{"family":"Plus Jakarta Sans"},"body":{"family":"Inter"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"4dos","hasAudioNarration":false,"actualSubdomain":"4dos","customDomains":["docs.fourdos.dev"],"gitSource":{"type":"github","owner":"pellartech","repo":"fourdos-monorepo","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":""},"entitlements":{},"buildId":"6af526ed66e069ab19a6e112","clientVersion":"0.0.4111","searchFilterCounts":[{"language":"default","tag":"API Reference","count":53},{"language":"default","tag":"Endpoints","count":52},{"language":"default","tag":"Documentation","count":28},{"language":"default","tag":"Modules","count":17},{"language":"default","tag":"Concepts","count":10},{"language":"default","tag":"Walkthroughs","count":9},{"language":"default","tag":"Users","count":9},{"language":"default","tag":"External integrations","count":7},{"language":"default","tag":"Imports","count":7},{"language":"default","tag":"Content API","count":4}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-rbac-roles","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":true},"customCss":[{"filePath":"style.css","content":"/* cyrillic-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxPcwhsk.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx_cwhsk.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx7cwhsk.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxPcwhsk.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx_cwhsk.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx7cwhsk.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxPcwhsk.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* vietnamese */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx_cwhsk.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx7cwhsk.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'JetBrains Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\nimg.nav-logo{height:2.5rem}code,pre,kbd,samp{font-family:\"JetBrains Mono\", ui-monospace, \"SF Mono\", Menlo, Consolas, monospace}"}],"page":{"pagePath":"authentication","slug":"/authentication","pageMetadata":{"title":"Authentication","description":"Organization API keys, publishable tracking keys, and how requests are scoped","href":"/authentication"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Organization API keys, publishable tracking keys, and how requests are scoped\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Table,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(_components.p,{children:`4D has four credential types. Which one you use depends on where the call is\nmade from:`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Credential`}),_jsx(_components.th,{children:`Prefix`}),_jsx(_components.th,{children:`Where it lives`}),_jsx(_components.th,{children:`What it’s for`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Organization API key`}),_jsx(_components.td,{children:_jsx(_components.code,{children:`org_`})}),_jsx(_components.td,{children:`Your servers (secret)`}),_jsxs(_components.td,{children:[`The partner REST API: users, imports, `,_jsx(_components.a,{href:`/concepts/consent`,children:`consents`}),`, `,_jsx(_components.a,{href:`/concepts/xp`,children:`XP and reward reads`}),`, module reads, revenue reporting and ad identity tokens`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Engagement tracking key`}),_jsx(_components.td,{children:_jsx(_components.code,{children:`ek_live_`})}),_jsx(_components.td,{children:`Client code (publishable)`}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`POST /v1/track`}),` event tracking, `,_jsx(_components.code,{children:`POST /v1/content-interactions`}),` reports and `,_jsx(_components.code,{children:`GET /v1/ads/serve`}),` `,_jsx(_components.a,{href:`/concepts/ads`,children:`publisher ad`}),` requests`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Content key`}),_jsx(_components.td,{children:_jsx(_components.code,{children:`ck_live_`})}),_jsx(_components.td,{children:`Client code (publishable)`}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`GET /v1/content/articles…`}),` published-article reads only`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Module credential`}),_jsx(_components.td,{children:_jsx(_components.code,{children:`mk_live_`})}),_jsx(_components.td,{children:`Module backends (secret)`}),_jsx(_components.td,{children:`Server-to-server event ingest with HMAC request signing`})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`organization-api-keys`,children:`Organization API keys`}),`\n`,_jsx(_components.p,{children:`Every partner API call authenticates with a bearer header:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_BASE_URL`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/users/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$USER_ID`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Authorization: Bearer `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_KEY`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The key is bound to your organization. There is no org id parameter anywhere\nin the API - your key `,_jsx(_components.em,{children:`is`}),` the tenancy. Isolation is enforced with\nrow-level security at the database layer, so a request can only ever read or\nwrite rows belonging to your organization.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Treat `,_jsx(_components.code,{children:`org_`}),` keys like passwords: server-side only, never in client bundles,\nrotate if exposed. Issue one from an authenticated partner admin session as described in the `,_jsx(_components.a,{href:`/quickstart`,children:`Quickstart`}),`.`]})}),`\n`,_jsxs(_components.p,{children:[`An `,_jsx(_components.code,{children:`org_`}),` key authenticates a service identity scoped to one organization. It cannot publish a module, install a module, issue a module credential, or mint another `,_jsx(_components.code,{children:`org_`}),` key. Use a partner admin session for those operations. When setting up a private module, author and publish it with the admin session, then mint the `,_jsx(_components.code,{children:`org_`}),` key afterward for any service that needs partner reads.`]}),`\n`,_jsx(Heading,{level:`2`,id:`admin-session-tokens`,children:`Admin session tokens`}),`\n`,_jsxs(_components.p,{children:[`Key management, tracking-key management and module authoring need a partner admin session rather than an `,_jsx(_components.code,{children:`org_`}),` key. A session token comes from signing in, either in the console or over the email OTP endpoints:`]}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`POST /v1/auth/email-otp/request`}),` with `,_jsx(_components.code,{children:`{\"email\": \"\u003cadmin email>\"}`}),` sends a one-time code to that address.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`POST /v1/auth/email-otp/verify`}),` with `,_jsx(_components.code,{children:`{\"email\": \"\u003cadmin email>\", \"otp\": \"\u003ccode>\"}`}),` returns a `,_jsx(_components.code,{children:`token`}),`, the `,_jsx(_components.code,{children:`user`}),`, and `,_jsx(_components.code,{children:`enrollmentRequired`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`If `,_jsx(_components.code,{children:`enrollmentRequired`}),` is `,_jsx(_components.code,{children:`true`}),`, the administrator has no authenticator yet. `,_jsx(_components.code,{children:`POST /api/totp/enroll`}),` with the token as a bearer returns `,_jsx(_components.code,{children:`secret`}),` (base32), `,_jsx(_components.code,{children:`otpauthUri`}),` (for a QR code) and `,_jsx(_components.code,{children:`backupCodes`}),`. Add the secret to an authenticator app and keep the backup codes; they are shown once. `,_jsx(_components.code,{children:`GET /api/totp/status`}),` with the same bearer answers `,_jsx(_components.code,{children:`{\"enrolled\": \u003cbool>, \"enrollmentRequired\": \u003cbool>}`}),` at any point.`]}),`\n`,_jsxs(_components.li,{children:[`Administrators must complete the second factor before any admin operation: `,_jsx(_components.code,{children:`POST /api/auth/two-factor/verify-totp`}),` with `,_jsx(_components.code,{children:`{\"code\": \"\u003cauthenticator code>\"}`}),` and the token as a bearer. Until then, admin routes answer `,_jsx(_components.code,{children:`401`}),` with the reason `,_jsx(_components.code,{children:`2FA_REQUIRED`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`POST /v1/session/active-organization`}),` with `,_jsx(_components.code,{children:`{\"organizationId\": \"\u003corganization id>\"}`}),` and the token as a bearer makes that organization the session’s active one. Sign-in picks the admin’s earliest seat on its own, so an administrator with seats on more than one organization does this before minting a key or publishing a module, or the key or module lands in the other organization.`]}),`\n`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`The first `,_jsx(_components.code,{children:`verify-totp`}),` after enrolment replaces the session. The token from step 2 stops working, and the new session token arrives in the `,_jsx(_components.code,{children:`set-auth-token`}),` response header. The `,_jsx(_components.code,{children:`token`}),` in that response’s body is the old one, so do not use it. Use the header value for step 5 and every call after it. Later sign-ins, with the authenticator already enrolled, keep the step 2 token and send no `,_jsx(_components.code,{children:`set-auth-token`}),` header.`]})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`6`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -i`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_BASE_URL`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/api/auth/two-factor/verify-totp\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Authorization: Bearer `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$ADMIN_TOKEN`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '{\"code\":\"123456\"}'`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# On first enrolment, read the new token from the response header:`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# set-auth-token: \u003cnew session token>`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Send the token as `,_jsx(_components.code,{children:`Authorization: Bearer \u003ctoken>`}),` on the key and module endpoints. A session belongs to the admin who signed in and acts on the session’s active organization; a key response carries that organization as `,_jsx(_components.code,{children:`organization_id`}),` and a module response as `,_jsx(_components.code,{children:`owner_organization_id`}),`, so check the one you meant. The token is not a credential to store or share.`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`export`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` ADMIN_TOKEN`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"\u003csession token from the sign-in steps above>\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_BASE_URL`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/api-keys\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Authorization: Bearer `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$ADMIN_TOKEN`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`publishable-tracking-keys`,children:`Publishable tracking keys`}),`\n`,_jsxs(_components.p,{children:[`Browser event tracking and publisher ad slots can’t hide a secret, so they\ndon’t use one. `,_jsx(_components.code,{children:`POST /v1/track`}),`, `,_jsx(_components.code,{children:`POST /v1/content-interactions`}),` and\n`,_jsx(_components.code,{children:`GET /v1/ads/serve`}),` (`,_jsx(_components.a,{href:`/concepts/ads`,children:`publisher ads`}),`) authenticate with a\n`,_jsx(_components.strong,{children:`publishable`}),` `,_jsx(_components.code,{children:`ek_live_…`}),` key that ships in client code, like an analytics\nmeasurement id. Abuse is bounded by two controls on the key itself, not by\nsecrecy:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`a `,_jsx(_components.strong,{children:`per-key origin allowlist`}),` - tracking and ad requests are only accepted\nfrom browser origins you configured for the key;`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`per-key rate limiting`}),`.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`You issue, list and revoke tracking keys with a partner admin session token\n(`,_jsx(_components.a,{href:`#admin-session-tokens`,children:`Admin session tokens`}),`), not with an `,_jsx(_components.code,{children:`org_`}),` key: an\n`,_jsx(_components.code,{children:`org_`}),` key gets `,_jsx(_components.code,{children:`403 FORBIDDEN`}),` on `,_jsx(_components.code,{children:`/v1/engagement/keys`}),`. The console has no\ntracking-key screen yet, so keys are managed over the API only. See\n`,_jsx(_components.a,{href:`/walkthroughs/trigger-an-event`,children:`Trigger an event`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`signed-in-fans-on-ad-requests`,children:`Signed-in fans on ad requests`}),`\n`,_jsxs(_components.p,{children:[`A publishable key identifies your site, not the fan reading it, so\n`,_jsx(_components.code,{children:`GET /v1/ads/serve`}),` never takes a member id from the page. To target a\nsigned-in fan, your server mints a signed identity with its organization API\nkey and hands it to the page:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` POST`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$API_BASE_URL`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/v1/ads/identity-tokens\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Authorization: Bearer `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$ORG_API_KEY`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Content-Type: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -d`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '{ \"member_external_id\": \"your-member-id\" }'`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# { \"identity_token\": \"ait1....\", \"expires_at\": \"2026-11-11T13:00:00.000Z\" }`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Name the fan by your own `,_jsx(_components.code,{children:`member_external_id`}),` or by the platform `,_jsx(_components.code,{children:`user_id`}),`,\nexactly one. The answer is `,_jsx(_components.code,{children:`404`}),` when no active member of your organization\nmatches. The token is opaque, bound to your organization and that member, and\nexpires after one hour, so mint a new one before then. The page passes it as\n`,_jsx(_components.code,{children:`identity_token`}),` on serve. A missing, expired, altered or other-club token\nserves the slot as if the fan were anonymous rather than failing, and so do\nthe unsigned `,_jsx(_components.code,{children:`member_external_id`}),` and `,_jsx(_components.code,{children:`user_id`}),` serve parameters, which no\nlonger identify anyone.`]}),`\n`,_jsx(_components.p,{children:`Impressions and clicks are recorded against the member only when the fan\nholds your club’s personalised content consent; without it they are counted\nanonymously. Sponsors never see which fan saw or clicked an ad.`}),`\n`,_jsx(Heading,{level:`2`,id:`content-keys`,children:`Content keys`}),`\n`,_jsxs(_components.p,{children:[`Reading a club’s published articles from its own website has the same problem\nand the same answer: a publishable `,_jsx(_components.code,{children:`ck_live_…`}),` key in the page, with a per-key\norigin allowlist as the control. You issue content keys in the console rather\nthan over the API. See `,_jsx(_components.a,{href:`/content/authentication`,children:`Content API authentication`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`module-credentials`,children:`Module credentials`}),`\n`,_jsxs(_components.p,{children:[`Installed modules push events server-to-server via `,_jsx(_components.code,{children:`POST /v1/ingest`}),` with an\n`,_jsx(_components.code,{children:`mk_live_...`}),` bearer credential plus an HMAC signature header (`,_jsx(_components.code,{children:`x-4d-signature`}),`) and an `,_jsx(_components.code,{children:`Idempotency-Key`}),`. Issue this credential from an admin session after installing a private module. See `,_jsx(_components.a,{href:`/walkthroughs/publish-a-private-module`,children:`Publish a private module`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`error-envelope`,children:`Error envelope`}),`\n`,_jsx(_components.p,{children:`All authenticated surfaces return errors in one envelope:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`7`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`7`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"error\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"code\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"UNAUTHORIZED\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"status\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`401`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"message\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"Authentication is required\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Missing or invalid credentials are a `,_jsx(_components.code,{children:`401`}),`; a valid credential without\npermission for the operation is a `,_jsx(_components.code,{children:`403`}),` or, where the resource’s existence\nshouldn’t be revealed, a `,_jsx(_components.code,{children:`404`}),`. `,_jsx(_components.code,{children:`code`}),` values are the stable contract to\nprogram against.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Authentication","description":"Organization API keys, publishable tracking keys, and how requests are scoped","href":"/authentication"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Organization API keys","slug":"organization-api-keys","depth":2,"children":[]},{"title":"Admin session tokens","slug":"admin-session-tokens","depth":2,"children":[]},{"title":"Publishable tracking keys","slug":"publishable-tracking-keys","depth":2,"children":[{"title":"Signed-in fans on ad requests","slug":"signed-in-fans-on-ad-requests","depth":3,"children":[]}]},{"title":"Content keys","slug":"content-keys","depth":2,"children":[]},{"title":"Module credentials","slug":"module-credentials","depth":2,"children":[]},{"title":"Error envelope","slug":"error-envelope","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"4dos","siteName":"4D OS Developer Docs","title":"Authentication - 4D OS Developer Docs","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-12T02:39:48.778Z","originalFileLocation":"authentication.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Organization API keys, publishable tracking keys, and how requests are scoped\">\u003cmeta data-mint-head name=\"application-name\" content=\"4D OS Developer Docs\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Authentication - 4D OS Developer Docs\">\u003cmeta data-mint-head property=\"og:description\" content=\"Organization API keys, publishable tracking keys, and how requests are scoped\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"4D OS Developer Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.fourdos.dev/authentication\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://4dos.mintlify.site/_mintlify/api/og/v2?division=Get+started\u0026amp;title=Authentication\u0026amp;description=Organization+API+keys%2C+publishable+tracking+keys%2C+and+how+requests+are+scoped\u0026amp;theme=b85e85529419e0832c327540\u0026amp;signature=c5eaef48879d12b252b7c595dfd0a688d1c2a357e0d6309c0fc12d35142b5387\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Authentication - 4D OS Developer Docs\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Organization API keys, publishable tracking keys, and how requests are scoped\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://4dos.mintlify.site/_mintlify/api/og/v2?division=Get+started\u0026amp;title=Authentication\u0026amp;description=Organization+API+keys%2C+publishable+tracking+keys%2C+and+how+requests+are+scoped\u0026amp;theme=b85e85529419e0832c327540\u0026amp;signature=c5eaef48879d12b252b7c595dfd0a688d1c2a357e0d6309c0fc12d35142b5387\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/4dos/ncF7JlfIXB4OFvnM/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"4D OS Developer Docs\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#1865D1\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.fourdos.dev/authentication\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/authentication.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.fourdos.dev/#organization\",\"name\":\"4D OS Developer Docs\",\"url\":\"https://docs.fourdos.dev\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/4dos/zox555bidSKp__2J/logo/light.svg?fit=max\u0026auto=format\u0026n=zox555bidSKp__2J\u0026q=85\u0026s=f664509c181b052b368f5e6d97c443cb\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.fourdos.dev#website\",\"name\":\"4D OS Developer Docs\",\"url\":\"https://docs.fourdos.dev\",\"publisher\":{\"@id\":\"https://docs.fourdos.dev/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.fourdos.dev/authentication#webpage\",\"url\":\"https://docs.fourdos.dev/authentication\",\"name\":\"Authentication\",\"description\":\"Organization API keys, publishable tracking keys, and how requests are scoped\",\"dateModified\":\"2026-11-12T02:39:48.778Z\",\"isPartOf\":{\"@id\":\"https://docs.fourdos.dev#website\"},\"breadcrumb\":{\"@id\":\"https://docs.fourdos.dev/authentication#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.fourdos.dev/authentication#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Get started\",\"item\":\"https://docs.fourdos.dev\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Authentication\",\"item\":\"https://docs.fourdos.dev/authentication\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.fourdos.dev/authentication#article\",\"headline\":\"Authentication\",\"name\":\"Authentication\",\"description\":\"Organization API keys, publishable tracking keys, and how requests are scoped\",\"url\":\"https://docs.fourdos.dev/authentication\",\"mainEntityOfPage\":{\"@id\":\"https://docs.fourdos.dev/authentication#webpage\"},\"dateModified\":\"2026-11-12T02:39:48.778Z\",\"publisher\":{\"@id\":\"https://docs.fourdos.dev/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.fourdos.dev#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"4D OS Developer Docs\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"4D OS Developer Docs\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/authentication.md","siteName":"4D OS Developer Docs","nav":{"global":null,"tabs":[{"tab":"Documentation","groups":[{"group":"Get started","pages":[{"title":"What is 4D OS?","href":""},{"title":"Quickstart","description":"From API key to your first member in five minutes","href":"/quickstart"},{"title":"Authentication","href":"/authentication"}]},{"group":"Concepts","pages":[{"title":"Members","description":"One person, one 4D identity - a member of each organization they engage with","href":"/concepts/members"},{"title":"Team","href":"/concepts/team"},{"title":"Events","href":"/concepts/events"},{"title":"Earning events","href":"/concepts/earning-events"},{"title":"XP","href":"/concepts/xp"},{"title":"Revenue","href":"/concepts/revenue"},{"title":"Sponsor draw entries","href":"/concepts/ad-entries"},{"title":"Consent","href":"/concepts/consent"},{"title":"Ads","href":"/concepts/ads"},{"title":"Tenants","href":"/concepts/tenants"}]},{"group":"Walkthroughs","pages":[{"title":"Create a user","href":"/walkthroughs/create-a-user"},{"title":"Trigger an event","href":"/walkthroughs/trigger-an-event"},{"title":"Get user details","href":"/walkthroughs/get-user-details"},{"title":"Read recent events","href":"/walkthroughs/read-recent-events"},{"title":"Receive webhooks","href":"/walkthroughs/receive-webhooks"},{"title":"Migrate an existing member base","href":"/walkthroughs/migrate-existing-members"},{"title":"Render a club's articles","href":"/walkthroughs/render-club-articles"},{"title":"Publish a private module","href":"/walkthroughs/publish-a-private-module"},{"title":"Record revenue","href":"/walkthroughs/record-revenue"}]},{"group":"Content API","pages":[{"title":"Content API overview","href":"/content/overview"},{"title":"Content API authentication","href":"/content/authentication"},{"title":"Articles","href":"/content/articles"},{"title":"Block format","href":"/content/blocks"}]},{"group":"SDK","pages":[{"title":"Engagement SDK","href":"/sdk/engagement-sdk"},{"title":"Ads SDK","href":"/sdk/ads-sdk"}]}]},{"tab":"API Reference","pages":[{"title":"API Reference","href":"/api-reference/overview"}]}]},"basePath":"","title":"Authentication - 4D OS Developer Docs","buildId":"dpl_DqYu5MPb49gWBBbUGgpHQUmCHY3P","deploymentId":"dpl_DqYu5MPb49gWBBbUGgpHQUmCHY3P","dynamics":[],"chunks":[]}