{"openapi":"3.1.0","info":{"title":"ShareShield API","version":"2.0.0","description":"One-time secret sharing for teams. Authenticate with an organization-bound API key in the `x-api-key` header. Every response carries `X-API-Version: 2`; errors use `{ error, code, details? }`; lists use `{ data, nextCursor }`."},"servers":[{"url":"/api/v2"}],"tags":[{"name":"Secrets","description":"Create, reveal, list and burn self-destructing secrets"},{"name":"Recipients","description":"Verify a recipient of a recipients-only secret by an emailed code"},{"name":"Requests","description":"Ask someone to send you a secret"},{"name":"Account","description":"The caller"},{"name":"Meta"}],"paths":{"/secrets":{"post":{"operationId":"createSecret","summary":"Create a secret","tags":["Secrets"],"security":[{"apiKey":["secrets:create"]},{"session":[]},{}],"responses":{"201":{"description":"Secret created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedSecret"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid, expired or revoked API key; or anonymous with `recipients` (UNAUTHORIZED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Plan quota reached, or expiry/maxViews above your plan ceiling (PAYMENT_REQUIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:create` (INSUFFICIENT_SCOPE); expiry/maxViews above your organization's policy ceiling, a passcode the policy requires is missing, or a recipient outside the allowed domains (FORBIDDEN; `details.disallowedDomains`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Anonymous daily limit reached, or recipient email limit reached","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Encrypts `content` and returns a one-time share link plus a recipient burn link. Anonymous callers are allowed (rate limited per IP); signed-in callers count against their plan quota. Expiry: send `expiryHours` or `expiryDays`. `expiryHours` wins when both are sent; with neither, your effective default applies (`GET /options`). Limits are plan ∩ organization policy: above a plan ceiling is 402, above a policy ceiling 403. Recipients: `recipients` (up to 10 addresses) emails each one the link; the response reports delivery per address in `recipients` and echoes `recipientsOnly`. With `recipientsOnly`, only a signed-in recipient or a caller holding a `recipientToken` (see `POST /secrets/{id}/recipient-code`) can open it. Signed-in callers only (anonymous: 401); your organization's recipient domain allow-list applies (403). Recipient emails count against per-user and per-organization rate limits (429).\n\nScope: `secrets:create`.\n\nRate limit: Anonymous: 2 per IP per UTC day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecretRequest"},"example":{"content":"db-password: hunter2","expiryHours":4,"maxViews":1}}}},"x-required-scope":"secrets:create"},"get":{"operationId":"listSecrets","summary":"List your secrets","tags":["Secrets"],"security":[{"apiKey":["secrets:list"]},{"session":[]}],"responses":{"200":{"description":"A page of secrets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretPage"}}}},"400":{"description":"Invalid cursor or filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:list` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Metadata of the secrets you created in this organization, newest first. Never includes content. Recipient addresses are not listed: `recipientCount` and `recipientsOnly` summarize them.\n\nScope: `secrets:list`.","parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from a previous page’s `nextCursor`","schema":{"type":"string","maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Page size (1-100)","schema":{"default":20,"type":"integer","minimum":1,"maximum":100}},{"name":"status","in":"query","required":false,"description":"active: can be revealed · viewed: opened at least once · expired · burned: destroyed by its owner, its recipient (burn link) or passcode lockout","schema":{"type":"string","enum":["active","viewed","expired","burned"]}}],"x-required-scope":"secrets:list"}},"/secrets/files":{"post":{"operationId":"createFileSecret","summary":"Create a file secret","tags":["Secrets"],"security":[{"apiKey":["secrets:create"]},{"session":[]}],"responses":{"201":{"description":"File secret created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedFileSecret"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"File sharing not on your plan, a trial, or billing inactive; plan quota reached; expiry/maxViews above your plan ceiling (PAYMENT_REQUIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:create` (INSUFFICIENT_SCOPE); files disabled by your organization's policy, expiry/maxViews above its ceiling, or a recipient outside the allowed domains (FORBIDDEN; `details.disallowedDomains`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File over your plan's size limit (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not multipart/form-data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Recipient email limit reached","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Uploads a file as a one-time secret. The only v2 endpoint that takes `multipart/form-data`: a `file` part and an optional `options` part holding JSON with the same options as text create (no `content`; unknown fields are rejected). Needs an active paid plan (free, trial and past-due organizations get 402) and an organization policy that allows files (403). Files over the plan's limit (`GET /options` → `maxFileSizeMb`) get 413, checked from Content-Length before the body is read. Recipients: `recipients` (up to 10 addresses) emails each one the link; the response reports delivery per address in `recipients` and echoes `recipientsOnly`. With `recipientsOnly`, only a signed-in recipient or a caller holding a `recipientToken` (see `POST /secrets/{id}/recipient-code`) can open it. Signed-in callers only (anonymous: 401); your organization's recipient domain allow-list applies (403). Recipient emails count against per-user and per-organization rate limits (429).\n\nScope: `secrets:create`.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","description":"The file (its name is kept, sanitized; its type is informational)"},"options":{"$ref":"#/components/schemas/FileSecretOptions","description":"Optional JSON options"}}},"encoding":{"options":{"contentType":"application/json"}}}}},"x-required-scope":"secrets:create"}},"/secrets/{id}":{"get":{"operationId":"getSecretMeta","summary":"Get secret metadata","tags":["Secrets"],"security":[{"apiKey":["secrets:read"]},{"session":[]},{}],"responses":{"200":{"description":"Secret metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretMeta"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid, expired or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:read` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"What a recipient can know before revealing: text or file (with name and size), whether a passcode, sign-in or recipient verification is needed, views left and expiry. Does **not** consume a view and never returns content. Public (anyone holding the id); API keys need `secrets:read`.\n\nScope: `secrets:read`.\n\nRate limit: 30 per IP per minute (shared with reveal and download).","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"x-required-scope":"secrets:read"},"patch":{"operationId":"renameSecret","summary":"Rename a secret","tags":["Secrets"],"security":[{"apiKey":["secrets:create"]},{"session":[]}],"responses":{"200":{"description":"The new name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatedSecret"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:create` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret, or not yours","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many ids that do not exist were tried","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Sets or clears your private name for a secret you own (`label`; null or empty clears it). Works at any point in its life, including after it was viewed or burned. The name is never shown to recipients.\n\nScope: `secrets:create`.\n\nRate limit: 20 misses (404) per IP per hour, shared with every lookup by id.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSecretRequest"},"example":{"label":"Prod DB for Sam"}}}},"x-required-scope":"secrets:create"},"delete":{"operationId":"burnSecret","summary":"Burn a secret","tags":["Secrets"],"security":[{"apiKey":["secrets:burn"]},{"session":[]}],"responses":{"204":{"description":"Burned"},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `secrets:burn` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret, or not yours","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many ids that do not exist were tried","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Destroys the content of a secret you own. Its metadata stays in your list with status `burned`.\n\nScope: `secrets:burn`.\n\nRate limit: 20 misses (404) per IP per hour, shared with every lookup by id.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"x-required-scope":"secrets:burn"}},"/secrets/{id}/reveal":{"post":{"operationId":"revealSecret","summary":"Reveal a secret","tags":["Secrets"],"security":[{"apiKey":["secrets:read"]},{"session":[]},{}],"responses":{"200":{"description":"The secret content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealedSecret"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR), or the secret is a file (FILE_SECRET; `details.download` is the download path)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid, expired or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Recipients-only secret without a signed-in recipient or valid `recipientToken` (RECIPIENT_VERIFICATION_REQUIRED; no view taken); password required or incorrect (FORBIDDEN; 5 wrong attempts burn the secret); or missing scope (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Decrypts a text secret and consumes one view; the last view burns it. Public (anyone holding the id); API keys need `secrets:read`. Responses are `Cache-Control: no-store`. The body is optional. File secrets return 400 `FILE_SECRET` without taking a view: download them instead. Recipients-only secrets (`GET /secrets/{id}` → `requiresRecipientVerification`) need a signed-in recipient or a `recipientToken`, as the `X-Recipient-Token` header or in the body.\n\nScope: `secrets:read`.\n\nRate limit: 30 per IP per minute.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}},{"name":"X-Recipient-Token","in":"header","required":false,"description":"Recipients-only secrets: the `recipientToken` from `POST /secrets/{id}/recipient-code/confirm` (alternative to the body field; the header wins)","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealSecretRequest"},"example":{"password":"correct horse"}}}},"x-required-scope":"secrets:read"}},"/secrets/{id}/download":{"post":{"operationId":"downloadSecretFile","summary":"Download a file secret","tags":["Secrets"],"security":[{"apiKey":["secrets:read"]},{"session":[]},{}],"responses":{"200":{"description":"The file","content":{"application/octet-stream":{"schema":{"type":"string","contentMediaType":"application/octet-stream"}}},"headers":{"Content-Disposition":{"description":"`attachment; filename=\"…\"; filename*=UTF-8''…` with the stored file name","schema":{"type":"string"}},"X-Views-Remaining":{"description":"Views left after this one; 0 means the secret is now burned","schema":{"type":"string"}},"X-Content-Type-Options":{"description":"`nosniff`","schema":{"type":"string"}},"Cache-Control":{"description":"`no-store`","schema":{"type":"string"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR), or the secret is text (NOT_A_FILE; `details.reveal` is the reveal path)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid, expired or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Recipients-only secret without a signed-in recipient or valid `recipientToken` (RECIPIENT_VERIFICATION_REQUIRED; no view taken); password required or incorrect (FORBIDDEN; 5 wrong attempts burn the secret); or missing scope (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Returns the file bytes and consumes one view, like reveal (same optional `{ \"password\" }` body). Always `application/octet-stream` with `Content-Disposition: attachment`, `X-Content-Type-Options: nosniff` and `Cache-Control: no-store`; never rendered inline. Text secrets return 400 `NOT_A_FILE` without taking a view. Recipients-only secrets need a `recipientToken`, as for reveal.\n\nScope: `secrets:read`.\n\nRate limit: 30 per IP per minute.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}},{"name":"X-Recipient-Token","in":"header","required":false,"description":"Recipients-only secrets: the `recipientToken` from `POST /secrets/{id}/recipient-code/confirm` (alternative to the body field; the header wins)","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealSecretRequest"}}}},"x-required-scope":"secrets:read"}},"/secrets/{id}/burn":{"post":{"operationId":"burnSecretWithToken","summary":"Burn a secret with its burn link","tags":["Secrets"],"security":[],"responses":{"204":{"description":"Burned"},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Wrong burn token (FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many attempts","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Lets a recipient destroy a secret without viewing it, using the `token` from the `burnUrl` returned at create. No authentication: the token is the credential. Afterwards the secret reveals as 410.\n\nRate limit: 30 per IP per minute.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BurnSecretRequest"},"example":{"token":"TOKEN_FROM_BURN_URL"}}}}}},"/secrets/{id}/recipient-code":{"post":{"operationId":"requestRecipientCode","summary":"Email a recipient verification code","tags":["Recipients"],"security":[],"responses":{"202":{"description":"Accepted (a code is sent if the address is a recipient)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientCodeAccepted"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many codes requested","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"For recipients-only secrets: emails an 8-digit code (valid 10 minutes) to `email` if it is one of the secret’s recipients. No authentication. Always 202 for a live secret, whether or not the address is a recipient, so recipients cannot be enumerated. A new code replaces a pending one, except within 60 seconds of sending it (the request is accepted, but the code already sent stays the valid one). Exchange it at `POST /secrets/{id}/recipient-code/confirm`.\n\nRate limit: 10 per IP per hour and 10 per secret per hour.","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientCodeRequest"},"example":{"email":"alex@example.com"}}}}}},"/secrets/{id}/recipient-code/confirm":{"post":{"operationId":"confirmRecipientCode","summary":"Exchange a recipient code for a token","tags":["Recipients"],"security":[],"responses":{"200":{"description":"Verified","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientVerification"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR), or the code is wrong or expired (INVALID_CODE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Already viewed, burned or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many wrong codes, the code was discarded (CODE_LOCKED); or rate limited (TOO_MANY_REQUESTS)","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Exchanges the emailed code for a `recipientToken` (valid 15 minutes, bound to this secret and address). Send it to reveal or download as the `X-Recipient-Token` header or body `recipientToken`. No authentication. A code works once; each wrong attempt uses one of its 5 attempts and the 5th discards it (429 `CODE_LOCKED`). After 20 wrong codes in total for an address, it can no longer be verified by code for this secret (429 `CODE_LOCKED` with `details.codesDisabled`); signing in with that address still opens the secret.\n\nRate limit: 30 per IP per minute (shared with reveal and download).","parameters":[{"name":"id","in":"path","required":true,"description":"Public secret id (the last segment of the share URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Public secret id, e.g. `k7mq-2vx-r9t`. Case, dashes and spaces are ignored, and `o`/`i`/`l` are read as `0`/`1`, so hand-typed ids work.","examples":["k7mq-2vx-r9t"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmRecipientCodeRequest"},"example":{"email":"alex@example.com","code":"123456"}}}}}},"/requests":{"post":{"operationId":"createRequest","summary":"Create a secret request","tags":["Requests"],"security":[{"apiKey":["requests:create"]},{"session":[]}],"responses":{"201":{"description":"Request created (`id`, `token`, `url`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedRequest"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `requests:create` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Request limit reached","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Creates a link someone can use to send you a secret. With `recipientEmail`, the link is emailed to them.\n\nScope: `requests:create`.\n\nRate limit: 20 per user per hour; 100 per organization per day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRequestRequest"},"example":{"recipientEmail":"alex@example.com","message":"Please send the staging DB password","expiryDays":3}}}},"x-required-scope":"requests:create"},"get":{"operationId":"listRequests","summary":"List your secret requests","tags":["Requests"],"security":[{"apiKey":["requests:read"]},{"session":[]}],"responses":{"200":{"description":"A page of requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPage"}}}},"400":{"description":"Invalid cursor or filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing scope `requests:read` or role permission (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Scope: `requests:read`.","parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from a previous page’s `nextCursor`","schema":{"type":"string","maxLength":256}},{"name":"limit","in":"query","required":false,"description":"Page size (1-100)","schema":{"default":20,"type":"integer","minimum":1,"maximum":100}},{"name":"status","in":"query","required":false,"description":"open: waiting for a secret · fulfilled · expired (never fulfilled)","schema":{"type":"string","enum":["open","fulfilled","expired"]}}],"x-required-scope":"requests:read"}},"/requests/{token}":{"get":{"operationId":"getRequest","summary":"Get a request (public)","tags":["Requests"],"security":[],"responses":{"200":{"description":"Request details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRequest"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already fulfilled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many links that do not exist were tried","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"What the person fulfilling a request sees, including what the requester's organization accepts: `requirePasscode` (send a `password`), `filesAllowed` and `maxFileSizeMb` (for `fulfil-file`). No authentication.\n\nRate limit: 20 misses (404) per IP per hour, shared with every lookup by id.","parameters":[{"name":"token","in":"path","required":true,"description":"Request token (the last segment of the request URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Request token"}}]}},"/requests/{token}/fulfil":{"post":{"operationId":"fulfilRequest","summary":"Fulfil a request (public)","tags":["Requests"],"security":[],"responses":{"201":{"description":"Fulfilled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FulfilledRequest"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Requester's plan quota reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"A passcode the requester's organization requires is missing: send `password` (FORBIDDEN; `GET /requests/{token}` → `requirePasscode`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already fulfilled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"JSON body over 1 MB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many fulfilments","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Creates a secret owned by the requester and emails them its link. No authentication.\n\nRate limit: 10 per IP per hour.","parameters":[{"name":"token","in":"path","required":true,"description":"Request token (the last segment of the request URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Request token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FulfilRequestRequest"},"example":{"content":"staging-db: s3cr3t","expiryDays":1}}}}}},"/requests/{token}/fulfil-file":{"post":{"operationId":"fulfilRequestWithFile","summary":"Fulfil a request with a file (public)","tags":["Requests"],"security":[],"responses":{"201":{"description":"Fulfilled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FulfilledRequest"}}}},"400":{"description":"Validation failed (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Files not on the requester's plan, a trial, or billing inactive; or the requester's quota is reached (PAYMENT_REQUIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Files disabled by the requester's policy, or a passcode it requires is missing (FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already fulfilled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File over the requester's size limit (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body is not multipart/form-data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many fulfilments","headers":{"Retry-After":{"description":"Seconds until the rate limit window ends (also in `details.retryAfterSeconds`). Absent on `CODE_LOCKED`, where waiting does not help: request a new code","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Like `fulfil`, but the response is a file: `multipart/form-data` with a `file` part and an optional `options` part holding JSON `{ expiryDays?, password? }` (unknown fields are rejected). No authentication. Gated on the **requester's** organization (the fulfiller is anonymous): an active paid plan (free, trial and past-due get 402), a policy that allows files (403) and its file size limit (413; `GET /requests/{token}` → `maxFileSizeMb`). Eligibility is checked before the body is read.\n\nRate limit: 10 per IP per hour (shared with fulfil).","parameters":[{"name":"token","in":"path","required":true,"description":"Request token (the last segment of the request URL)","schema":{"type":"string","minLength":1,"maxLength":64,"description":"Request token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","description":"The file (its name is kept, sanitized; its type is informational)"},"options":{"$ref":"#/components/schemas/FulfilFileOptions","description":"Optional JSON options"}}},"encoding":{"options":{"contentType":"application/json"}}}}}}},"/options":{"get":{"operationId":"getSecretOptions","summary":"Your limits and defaults","tags":["Secrets"],"security":[{"apiKey":[]},{"session":[]},{}],"responses":{"200":{"description":"Effective limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretOptions"}}}},"401":{"description":"Invalid, expired or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"The effective create limits and defaults for these credentials (plan ∩ organization policy; anonymous callers get the free plan). Use it to pre-validate expiry, views, file size and recipients (`maxRecipients`, `allowedRecipientDomains`): `limitedBy` says whether exceeding a ceiling is 402 (plan) or 403 (policy)."}},"/me":{"get":{"operationId":"getMe","summary":"Who am I","tags":["Account"],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"The caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Missing, invalid, expired or revoked credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"The caller, their organization, and the scopes and permissions usable with these credentials."}},"/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This OpenAPI document","tags":["Meta"],"security":[],"responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message"},"code":{"type":"string","description":"Stable machine-readable code: VALIDATION_ERROR, INVALID_CURSOR, BAD_REQUEST, FILE_SECRET, NOT_A_FILE, INVALID_CODE, UNAUTHORIZED, PAYMENT_REQUIRED, FORBIDDEN, TWO_FACTOR_REQUIRED, CROSS_SITE_REQUEST, INSUFFICIENT_SCOPE, RECIPIENT_VERIFICATION_REQUIRED, NOT_FOUND, CONFLICT, GONE, PAYLOAD_TOO_LARGE, UNSUPPORTED_MEDIA_TYPE, TOO_MANY_REQUESTS, CODE_LOCKED, INTERNAL_ERROR (see the error code table at /docs/api)"},"details":{"description":"Optional structured detail"}},"required":["error","code"],"additionalProperties":false},"CreateSecretRequest":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":65536,"description":"The secret text","examples":["hunter2"]},"label":{"type":"string","maxLength":120,"description":"Your private name for the secret (e.g. \"Prod DB for Sam\"), shown in your lists and owner emails, never to recipients. Signed-in callers and API keys only","examples":["Prod DB for Sam"]},"expiryHours":{"description":"Lifetime in hours (1-2160). Takes precedence over `expiryDays` when both are sent. Default: your organization's default (`GET /options` → `defaultExpiryHours`)","type":"integer","minimum":1,"maximum":2160},"expiryDays":{"description":"Lifetime in days (1-90). Ignored when `expiryHours` is sent","type":"integer","minimum":1,"maximum":90},"maxViews":{"description":"Reveals allowed before the secret burns (1-100). Default: `GET /options` → `defaultMaxViews` (usually 1)","type":"integer","minimum":1,"maximum":100},"password":{"type":"string","minLength":1,"maxLength":256,"description":"Passcode the recipient must supply to reveal the secret"},"requireSignIn":{"default":false,"description":"Only signed-in ShareShield users (or API keys) can reveal","type":"boolean"},"notifyOnOpen":{"default":false,"description":"Email when the secret is opened","type":"boolean"},"notifyEmail":{"description":"Where to send the open notification (default: your email)","type":"string","maxLength":254,"format":"email"},"notifyEmails":{"description":"Up to 5 addresses to notify when the secret is opened (merged with `notifyEmail`, deduplicated). Sending any enables the notification","maxItems":5,"type":"array","items":{"type":"string","maxLength":254,"format":"email"}},"recipients":{"description":"Email the share link to up to 10 addresses (deduplicated case-insensitively). Signed-in callers only (anonymous: 401). Your organization's recipient domain allow-list applies (403 `details.disallowedDomains`; see `GET /options` → `allowedRecipientDomains`). Delivery status per address is returned in `recipients`","maxItems":10,"type":"array","items":{"type":"string","maxLength":254,"format":"email"}},"recipientsOnly":{"description":"Only the listed recipients can open it: signed in as a recipient, or with a `recipientToken` from `POST /secrets/{id}/recipient-code/confirm`. Needs at least one recipient. Default false","type":"boolean"}},"required":["content"],"additionalProperties":false},"CreatedSecret":{"type":"object","properties":{"id":{"type":"string","description":"Public secret id as in the share URL, e.g. `k7mq-2vx-r9t` (10 characters, grouped)","examples":["k7mq-2vx-r9t"]},"url":{"type":"string","format":"uri","description":"Share link for the recipient"},"burnUrl":{"description":"Burn link: lets the recipient destroy the secret without viewing it. Returned only here, at create; the token is not stored in clear and cannot be recovered","type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"maxViews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Reveals allowed (the effective value after defaults)"},"recipients":{"description":"Present only when `recipients` were sent: delivery status per address","type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Normalized (lower-cased) recipient address"},"sent":{"type":"boolean","description":"The link email was handed to the mail provider. false: sending failed; the secret was still created"}},"required":["email","sent"],"additionalProperties":false}},"recipientsOnly":{"description":"Present only when `recipients` were sent","type":"boolean"}},"required":["id","url","expiresAt","maxViews"],"additionalProperties":false},"FileSecretOptions":{"type":"object","properties":{"label":{"type":"string","maxLength":120,"description":"Your private name for the secret (e.g. \"Prod DB for Sam\"), shown in your lists and owner emails, never to recipients. Signed-in callers and API keys only","examples":["Prod DB for Sam"]},"expiryHours":{"description":"Lifetime in hours (1-2160). Takes precedence over `expiryDays` when both are sent. Default: your organization's default (`GET /options` → `defaultExpiryHours`)","type":"integer","minimum":1,"maximum":2160},"expiryDays":{"description":"Lifetime in days (1-90). Ignored when `expiryHours` is sent","type":"integer","minimum":1,"maximum":90},"maxViews":{"description":"Reveals allowed before the secret burns (1-100). Default: `GET /options` → `defaultMaxViews` (usually 1)","type":"integer","minimum":1,"maximum":100},"password":{"type":"string","minLength":1,"maxLength":256,"description":"Passcode the recipient must supply to reveal the secret"},"requireSignIn":{"default":false,"description":"Only signed-in ShareShield users (or API keys) can reveal","type":"boolean"},"notifyOnOpen":{"default":false,"description":"Email when the secret is opened","type":"boolean"},"notifyEmail":{"description":"Where to send the open notification (default: your email)","type":"string","maxLength":254,"format":"email"},"notifyEmails":{"description":"Up to 5 addresses to notify when the secret is opened (merged with `notifyEmail`, deduplicated). Sending any enables the notification","maxItems":5,"type":"array","items":{"type":"string","maxLength":254,"format":"email"}},"recipients":{"description":"Email the share link to up to 10 addresses (deduplicated case-insensitively). Signed-in callers only (anonymous: 401). Your organization's recipient domain allow-list applies (403 `details.disallowedDomains`; see `GET /options` → `allowedRecipientDomains`). Delivery status per address is returned in `recipients`","maxItems":10,"type":"array","items":{"type":"string","maxLength":254,"format":"email"}},"recipientsOnly":{"description":"Only the listed recipients can open it: signed in as a recipient, or with a `recipientToken` from `POST /secrets/{id}/recipient-code/confirm`. Needs at least one recipient. Default false","type":"boolean"}},"additionalProperties":false},"CreatedFileSecret":{"type":"object","properties":{"id":{"type":"string","description":"Public secret id as in the share URL, e.g. `k7mq-2vx-r9t` (10 characters, grouped)","examples":["k7mq-2vx-r9t"]},"url":{"type":"string","format":"uri","description":"Share link for the recipient"},"burnUrl":{"description":"Burn link: lets the recipient destroy the secret without viewing it. Returned only here, at create; the token is not stored in clear and cannot be recovered","type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"maxViews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Reveals allowed (the effective value after defaults)"},"recipients":{"description":"Present only when `recipients` were sent: delivery status per address","type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Normalized (lower-cased) recipient address"},"sent":{"type":"boolean","description":"The link email was handed to the mail provider. false: sending failed; the secret was still created"}},"required":["email","sent"],"additionalProperties":false}},"recipientsOnly":{"description":"Present only when `recipients` were sent","type":"boolean"},"kind":{"type":"string","const":"file"},"fileName":{"type":"string","description":"The stored (sanitized) file name the recipient downloads"},"sizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"File size in bytes"}},"required":["id","url","expiresAt","maxViews","kind","fileName","sizeBytes"],"additionalProperties":false},"SecretMeta":{"type":"object","properties":{"kind":{"type":"string","enum":["text","file"],"description":"text: reveal it · file: download it"},"fileName":{"description":"File secrets only","type":"string"},"sizeBytes":{"description":"File secrets only","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"requiresPassword":{"type":"boolean","description":"A passcode must be sent to reveal/download"},"requiresAuth":{"type":"boolean","description":"Only signed-in users or API keys can reveal/download"},"requiresRecipientVerification":{"type":"boolean","description":"Recipients-only: reveal/download needs a signed-in recipient or a `recipientToken` (`POST /secrets/{id}/recipient-code`), otherwise 403 RECIPIENT_VERIFICATION_REQUIRED"},"viewsRemaining":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]}},"required":["kind","requiresPassword","requiresAuth","requiresRecipientVerification","viewsRemaining","expiresAt"],"additionalProperties":false},"BurnSecretRequest":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":256,"description":"The `token` query parameter of the `burnUrl` returned at create"}},"required":["token"],"additionalProperties":false},"UpdateSecretRequest":{"type":"object","properties":{"label":{"anyOf":[{"type":"string","maxLength":120,"description":"Your private name for the secret (e.g. \"Prod DB for Sam\"), shown in your lists and owner emails, never to recipients. Signed-in callers and API keys only","examples":["Prod DB for Sam"]},{"type":"null"}]}},"required":["label"],"additionalProperties":false},"UpdatedSecret":{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["label"],"additionalProperties":false},"SecretOptions":{"type":"object","properties":{"maxExpiryHours":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Longest allowed lifetime; above it create returns 402 (plan) or 403 (policy)"},"defaultExpiryHours":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Used when neither expiryHours nor expiryDays is sent"},"maxViews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"defaultMaxViews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"requirePasscode":{"type":"boolean","description":"The org policy requires a password on every secret (403 otherwise)"},"allowAnonymousViewers":{"type":"boolean","description":"false: every secret is created as sign-in only"},"filesAllowed":{"type":"boolean"},"maxFileSizeMb":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"0 when files are not allowed"},"filesBlockedReason":{"description":"Why files are not allowed (anonymous: 401 · plan, trial, billing_inactive: 402 · policy: 403)","type":"string","enum":["anonymous","plan","trial","billing_inactive","policy"]},"defaultNotifyOnOpen":{"type":"boolean"},"allowSecretRequests":{"type":"boolean"},"allowedRecipientDomains":{"type":"array","items":{"type":"string"},"description":"Recipient domain allow-list from the org policy (subdomains match); empty means any domain. Others get 403 with `details.disallowedDomains`"},"maxRecipients":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Most `recipients` per secret; 0 for anonymous callers (who cannot add any)"},"limitedBy":{"type":"object","properties":{"maxExpiryHours":{"type":"string","enum":["plan","policy"]},"maxViews":{"type":"string","enum":["plan","policy"]}},"required":["maxExpiryHours","maxViews"],"additionalProperties":false,"description":"Which side sets each ceiling: plan (exceeding it is 402) or policy (403)"}},"required":["maxExpiryHours","defaultExpiryHours","maxViews","defaultMaxViews","requirePasscode","allowAnonymousViewers","filesAllowed","maxFileSizeMb","defaultNotifyOnOpen","allowSecretRequests","allowedRecipientDomains","maxRecipients","limitedBy"],"additionalProperties":false},"RevealSecretRequest":{"type":"object","properties":{"password":{"type":"string","minLength":1,"maxLength":256,"description":"Passcode the recipient must supply to reveal the secret"},"recipientToken":{"type":"string","minLength":1,"maxLength":1024,"description":"Recipients-only secrets: the token from `POST /secrets/{id}/recipient-code/confirm`. May be sent as the `X-Recipient-Token` header instead (the header wins)"}},"additionalProperties":false},"RevealedSecret":{"type":"object","properties":{"content":{"type":"string","description":"The decrypted secret text"},"viewsRemaining":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Views left after this one; 0 means the secret is now burned"}},"required":["content","viewsRemaining"],"additionalProperties":false},"RecipientCodeRequest":{"type":"object","properties":{"email":{"description":"The recipient address to send the 8-digit code to","type":"string"}},"required":["email"],"additionalProperties":false},"RecipientCodeAccepted":{"type":"object","properties":{"accepted":{"type":"boolean","const":true},"message":{"type":"string","description":"The same whether or not the address is a recipient"}},"required":["accepted","message"],"additionalProperties":false},"ConfirmRecipientCodeRequest":{"type":"object","properties":{"email":{"description":"The address the code was sent to","type":"string"},"code":{"type":"string","pattern":"^\\d{8}$","description":"The 8-digit code from the email","examples":["12345678"]}},"required":["email","code"],"additionalProperties":false},"RecipientVerification":{"type":"object","properties":{"recipientToken":{"type":"string","description":"Send as the `X-Recipient-Token` header (or body `recipientToken`) to reveal or download. Bound to this secret and address"},"expiresAt":{"type":"string","format":"date-time","description":"When the token stops working (15 minutes)","examples":["2026-10-02T12:00:00.000Z"]}},"required":["recipientToken","expiresAt"],"additionalProperties":false},"SecretPage":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Your private name for the secret, if any"},"status":{"type":"string","enum":["active","viewed","expired","burned"],"description":"active: can be revealed · viewed: opened at least once · expired · burned: destroyed by its owner, its recipient (burn link) or passcode lockout"},"kind":{"anyOf":[{"type":"string","enum":["text","file"],"description":"text: reveal it · file: download it"},{"type":"null"}],"description":"text or file; null once the content has been destroyed (the payload, and with it the file name and size, is deleted)"},"fileName":{"description":"File secrets whose content still exists","type":"string"},"sizeBytes":{"description":"File secrets whose content still exists","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"maxViews":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"viewsRemaining":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"0 once burned, viewed for the last time or expired"},"isPasswordProtected":{"type":"boolean","description":"A passcode is set"},"requireSignIn":{"type":"boolean"},"recipientsOnly":{"type":"boolean","description":"Only its email recipients can open it"},"recipientCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Email recipients the link was sent to (addresses are not listed)"},"openedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},{"type":"null"}]},"burnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},{"type":"null"}]},"burnReason":{"anyOf":[{"type":"string","enum":["viewed","owner","recipient","passcode_lockout","expired"],"description":"Why the content was destroyed: last view taken · owner burned it · recipient used the burn link · too many wrong passcodes · expired"},{"type":"null"}]},"fromRequest":{"type":"boolean","description":"Created by fulfilling one of your secret requests"}},"required":["id","url","label","status","kind","createdAt","expiresAt","maxViews","viewsRemaining","isPasswordProtected","requireSignIn","recipientsOnly","recipientCount","openedAt","burnedAt","burnReason","fromRequest"],"additionalProperties":false}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to fetch the next page; null on the last page"}},"required":["data","nextCursor"],"additionalProperties":false},"CreateRequestRequest":{"type":"object","properties":{"recipientEmail":{"description":"Email the request link to this address","type":"string","maxLength":254,"format":"email"},"message":{"description":"Shown to the person fulfilling the request","type":"string","maxLength":500},"expiryDays":{"default":3,"description":"How long the request link stays open (days)","type":"integer","minimum":1,"maximum":90},"notifyOnFulfill":{"default":true,"description":"Email you the secret link when fulfilled","type":"boolean"}},"additionalProperties":false},"CreatedRequest":{"type":"object","properties":{"id":{"type":"string","description":"Request id (stable; appears in audit events)"},"token":{"type":"string","description":"Request token (as in the request URL)"},"url":{"type":"string","format":"uri","description":"Link to send to the person who holds the secret"}},"required":["id","token","url"],"additionalProperties":false},"PublicRequest":{"type":"object","properties":{"requesterName":{"type":"string"},"requesterEmail":{"type":"string"},"message":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"requirePasscode":{"type":"boolean","description":"The requester's organization requires a `password` on the response (403 otherwise)"},"filesAllowed":{"type":"boolean","description":"The response can be a file (`POST /requests/{token}/fulfil-file`)"},"maxFileSizeMb":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Largest file the requester accepts; 0 when files are not allowed"}},"required":["requesterName","requesterEmail","message","createdAt","expiresAt","requirePasscode","filesAllowed","maxFileSizeMb"],"additionalProperties":false},"FulfilRequestRequest":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":65536,"description":"The secret text","examples":["hunter2"]},"expiryDays":{"default":1,"description":"Lifetime of the created secret (days)","type":"integer","minimum":1,"maximum":90},"password":{"type":"string","minLength":1,"maxLength":256,"description":"Passcode the recipient must supply to reveal the secret"}},"required":["content"],"additionalProperties":false},"FulfilFileOptions":{"type":"object","properties":{"expiryDays":{"default":1,"description":"Lifetime of the created secret (days)","type":"integer","minimum":1,"maximum":90},"password":{"type":"string","minLength":1,"maxLength":256,"description":"Passcode the recipient must supply to reveal the secret"}},"additionalProperties":false},"FulfilledRequest":{"type":"object","properties":{"fulfilled":{"type":"boolean","const":true},"message":{"type":"string"}},"required":["fulfilled","message"],"additionalProperties":false},"RequestPage":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string","format":"uri"},"status":{"type":"string","enum":["open","fulfilled","expired"],"description":"open: waiting for a secret · fulfilled · expired (never fulfilled)"},"recipientEmail":{"anyOf":[{"type":"string"},{"type":"null"}]},"message":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},"fulfilledAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)","examples":["2026-10-02T12:00:00.000Z"]},{"type":"null"}]}},"required":["token","url","status","recipientEmail","message","createdAt","expiresAt","fulfilledAt"],"additionalProperties":false}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` to fetch the next page; null on the last page"}},"required":["data","nextCursor"],"additionalProperties":false},"Me":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"displayName":{"type":"string"},"role":{"type":"string","description":"Org role: owner, admin, member or viewer"},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"via":{"type":"string","enum":["session","apiKey"]},"apiKey":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["secrets:create","secrets:read","secrets:burn","secrets:list","requests:create","requests:read","audit:read"],"description":"API key scope"}}},"required":["id","scopes"],"additionalProperties":false},{"type":"null"}],"description":"The calling key; null for session callers"},"scopes":{"type":"array","items":{"type":"string","enum":["secrets:create","secrets:read","secrets:burn","secrets:list","requests:create","requests:read","audit:read"],"description":"API key scope"},"description":"Scopes usable right now (role ∩ key scopes)"},"permissions":{"type":"array","items":{"type":"string"},"description":"Effective role permissions as resource:action strings"}},"required":["id","email","displayName","role","organization","via","apiKey","scopes","permissions"],"additionalProperties":false}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Organization-bound key (`shs_…`) created under Profile → API keys. Effective permission is your role ∩ the key scopes: `secrets:create` (Create self-destructing secrets and files in the organization.); `secrets:read` (Reveal (and so consume a view of) a secret by its id.); `secrets:burn` (Destroy secrets the key owner created.); `secrets:list` (List metadata of the key owner's secrets (never their content).); `requests:create` (Ask someone to send you a secret.); `requests:read` (List the key owner's secret requests.); `audit:read` (Read the organization's audit log (owners and admins).)"},"session":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Browser session of the ShareShield web app (same-origin use only)."}}}}