{"openapi":"3.1.0","info":{"title":"dropthis API","description":"dropthis is the publish layer between AI and the internet. Send content (HTML, files, or a URL to fetch) and get back a permanent shareable URL — no git, no build step, no config.\n\n**Conventions agents most often get wrong**\n\n- Publishing creates a NEW drop at a NEW URL every time. To change something you already published, UPDATE it (`PATCH /v1/drops/{id}` for settings, `POST /v1/drops/{id}/deployments` for content) — never publish again, that makes a duplicate.\n- Keep the `drop_…` id from the publish response. Updating, reading, or deleting needs that full id — the URL slug is not the id. If you only have a URL/slug, resolve it first via `POST /v1/drops/resolve`.\n- Updating content is patch-by-default: files you don't mention are kept. Pass `mode: replace` to swap the entire file set.\n- For images, video, PDFs, or fonts, pass a per-file `source_url` (fetched server-side) instead of base64-inlining bytes.\n- Free drops expire after 30 days; paid plans keep them. Password protection and custom domains are Pro features.\n\n**Errors** use RFC 7807 `application/problem+json` (the `Problem` schema). Branch on `code`, gate retries on `retryable`, and show `suggestion` to the user. `code` is an OPEN vocabulary (80+ values, growing) — treat an unknown code as the generic class for the status. The most common values (not exhaustive) are:\n\n- `validation_error`\n- `authentication_required`\n- `invalid_session`\n- `not_found`\n- `conflict`\n- `revision_conflict`\n- `feature_not_in_plan`\n- `quota_exceeded`\n- `insufficient_scope`\n- `step_up_required`\n- `invalid_api_key`\n- `otp_expired`\n- `otp_invalid`\n- `rate_limit_exceeded`\n- `blocked_domain`\n- `account_not_found`\n- `no_assignable_domain`\n- `edge_contract_violation`\n- `edge_sync_failed`\n- `remote_fetch_failed`\n- `ingest_guard_exceeded`\n- `workspace_mismatch`\n- `workspace_selector_not_allowed`\n- `service_cannot_mint_delegated`\n- `sole_owner_of_team`\n- `http_error`\n- `server_error`","version":"0.1.0"},"servers":[{"url":"https://api.dropthis.app"}],"paths":{"/v1/auth/email/otp":{"post":{"tags":["Auth"],"summary":"Request email OTP","description":"Send a six-digit email OTP for a short-lived session bootstrap.","operationId":"request_email_otp_v1_auth_email_otp_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailOtpRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OtpSentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/email/verify":{"post":{"tags":["Auth"],"summary":"Verify email OTP","description":"Verify an email OTP and issue a short-lived session token.","operationId":"verify_email_otp_v1_auth_email_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"401":{"description":"otp_expired (no active code) or otp_invalid (wrong code)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/handoff":{"post":{"tags":["Auth"],"summary":"Mint an auto-login handoff code","description":"Mint a single-use, ~60s code that hands the holder's session off to the console. Only a console/extension SESSION principal may mint one — an API key or delegated grant cannot, since redeem starts a full console session.","operationId":"create_handoff_v1_auth_handoff_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffResponse"}}}},"401":{"description":"session_required — the caller is not an interactive session","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/auth/handoff/redeem":{"post":{"tags":["Auth"],"summary":"Redeem an auto-login handoff code","description":"Exchange a single-use handoff code for a fresh console session. Unauthenticated by design (the caller has no session yet); the bound `state` must arrive in the `dt_handoff_state` cookie (CSRF guard) and the code is burned on first use.","operationId":"redeem_handoff_v1_auth_handoff_redeem_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffRedeemRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"401":{"description":"handoff_invalid — expired, already used, or state mismatch","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/session":{"delete":{"tags":["Auth"],"summary":"End session","description":"Revoke the presented at_ session token. The token is rejected on all subsequent requests. API keys do not have sessions; revoke them via DELETE /v1/api-keys/{key_id}.","operationId":"logout_v1_auth_session_delete","responses":{"204":{"description":"Successful Response"},"400":{"description":"Authenticated with an API key instead of an at_ session token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh a console session","description":"Exchange a rotating refresh token for a fresh access+refresh pair. Each refresh rotates: the presented refresh token is invalidated. Replaying an already-used token invalidates the whole session family and returns 401 invalid_session.","operationId":"refresh_session_v1_auth_refresh_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"401":{"description":"Invalid, used, expired, or revoked refresh token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/sessions":{"get":{"tags":["Auth"],"summary":"List active sessions","description":"List the authenticated account's active console sessions. The session making the request is flagged with current=true.","operationId":"list_sessions_v1_auth_sessions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionListResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/auth/sessions/{session_id}":{"delete":{"tags":["Auth"],"summary":"Revoke a session","description":"Revoke one of the authenticated account's sessions (sign out that device).","operationId":"revoke_session_v1_auth_sessions__session_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"Session identifier to revoke","title":"Session Id"},"description":"Session identifier to revoke"}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"The addressed resource does not exist or is not visible to this credential.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/sessions/revoke-all":{"post":{"tags":["Auth"],"summary":"Revoke all sessions","description":"Sign out everywhere: revoke every console session for the authenticated account.","operationId":"revoke_all_sessions_v1_auth_sessions_revoke_all_post","responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/auth/step-up":{"post":{"tags":["Auth"],"summary":"Re-authenticate for a destructive action","description":"Elevate the current console session by verifying a one-time code. Destructive actions (deleting drops or domains, revoking API keys or sessions, changing the default/dedicated domain) require a fresh step-up within the last few minutes. Requires a session token; the OTP must be for the authenticated account's own email — a code for a different account does NOT elevate the session. API keys never need step-up.","operationId":"step_up_v1_auth_step_up_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"400":{"description":"Authenticated with an API key (no session to elevate)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"otp_expired, otp_invalid, or an OTP for a different account (step_up_account_mismatch)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/api-keys":{"get":{"tags":["API Keys"],"summary":"List API keys","description":"List active API keys for the authenticated workspace. Raw key secrets are never returned.","operationId":"list_api_keys_v1_api_keys_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyListResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["API Keys"],"summary":"Create API key","description":"Create a durable sk_ API key for CLI, SDK, MCP, or automation use. The raw key is returned only once. Supports the Idempotency-Key header with a 15-minute replay window.","operationId":"create_api_key_v1_api_keys_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}}}},"403":{"description":"A pinned service key cannot mint a delegated credential (escalation guard)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency-Key conflict (reused with a different body, or in progress)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"allowed_workspace_ids contains a workspace the account does not belong to, or the 'mcp:' label prefix is reserved for the MCP worker","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required, or the supplied credential is invalid or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/api-keys/{key_id}":{"delete":{"tags":["API Keys"],"summary":"Revoke API key","description":"Revoke an API key in the authenticated workspace. Revoking your OWN key needs `credentials:write` (a default login has it); revoking ANOTHER member's key in a shared workspace needs `credentials:admin` (never bundled). Missing, foreign, and already-revoked keys return not found.","operationId":"revoke_api_key_v1_api_keys__key_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","description":"API key identifier to revoke","title":"Key Id"},"description":"API key identifier to revoke"}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required, or the supplied credential is invalid or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"The addressed resource does not exist or is not visible to this credential.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/uploads":{"post":{"tags":["Uploads"],"summary":"Create upload session","description":"Create a staged upload session for local files, folders, binaries, or generated artifacts that should not be sent through inline publish. The request declares a file manifest before any bytes are uploaded; Dropthis validates plan limits, path safety, duplicate paths, entry file membership, and active upload-session caps. The response returns direct storage upload targets plus an upload_id; it does not create or mutate a drop until the client uploads bytes, completes the session, and publishes with POST /v1/drops or a deployment endpoint. Use Idempotency-Key when retrying session creation.","operationId":"create_upload_session","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadSessionRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadSessionResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Upload session conflict or idempotency key reuse","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid upload manifest or verification failure","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many active upload sessions","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/uploads/{upload_id}":{"get":{"tags":["Uploads"],"summary":"Get upload session","description":"Return owner-visible status and manifest details for one staged upload session. Use this to recover file IDs, paths, object keys, verification state, and the entry file before completing or publishing. The endpoint does not list all uploads and does not refresh signed upload URLs; create a new upload session if the signed URLs have expired. Unknown or cross-account upload IDs are always reported as not found.","operationId":"get_upload_session","security":[{"HTTPBearer":[]}],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404.","title":"Upload Id"},"description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Upload session not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Uploads"],"summary":"Cancel upload session","description":"Cancel an unpublished staged upload session and mark its staging objects for cleanup. This is for abandoning created, completed, failed, or expired sessions before they are consumed by publish; it returns no response body. Publishing or already published sessions cannot be cancelled because they may be tied to a durable drop or deployment. Unknown or cross-account upload IDs are always reported as not found.","operationId":"cancel_upload_session","security":[{"HTTPBearer":[]}],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404.","title":"Upload Id"},"description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404."}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Upload session not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Upload session conflict or idempotency key reuse","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/uploads/{upload_id}/complete":{"post":{"tags":["Uploads"],"summary":"Complete upload session","description":"Verify staged objects and mark an upload session complete so it can be published through POST /v1/drops or a deployment endpoint. Clients upload bytes directly to storage first using the signed targets from create_upload_session; this endpoint takes no request body. Completion checks object existence, size, content type, and any declared checksum_sha256 against the manifest and fails the session if verification does not match. Use Idempotency-Key when retrying completion; sessions past the completion grace window return 410.","operationId":"complete_upload_session","security":[{"HTTPBearer":[]}],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404.","title":"Upload Id"},"description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Upload session not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Upload session conflict or idempotency key reuse","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"410":{"description":"Upload session expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid upload manifest or verification failure","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/uploads/{upload_id}/ingest":{"post":{"tags":["Uploads"],"summary":"Ingest remote-fetch files","description":"Fetch every source_url file declared in the upload manifest into staging on the server's behalf. Call this after create_upload_session and before complete_upload_session whenever the manifest declares remote-fetch (source_url) files; client_put files are uploaded by the client to their signed targets and are not touched here. The endpoint takes no request body and runs synchronously: it HEAD-probes and fetches each URL under plan size and ingest budget guards, stages the bytes, and records each file's discovered content type and size. If any remote file cannot be fetched, the call returns 422 remote_fetch_failed listing the failed paths and the session stays resumable, so retrying after fixing the source_url(s) re-runs only the still pending files. Use Idempotency-Key when retrying; a failed ingest is never cached, so a fixed retry with the same key re-runs. Per-account ingest calls are rate limited (429).","operationId":"ingest_upload_session","security":[{"HTTPBearer":[]}],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404.","title":"Upload Id"},"description":"Upload session identifier returned by create_upload_session. Unknown or cross-account upload IDs return 404."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestSessionResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Upload session not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Upload session conflict or idempotency key reuse","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"410":{"description":"Upload session expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid upload manifest or verification failure","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many active upload sessions","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops":{"post":{"tags":["Drops"],"summary":"Publish a drop","description":"Publishes a drop from a completed staged upload session or a server-fetched source_url, and returns a permanent drop URL.","operationId":"create_drop","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"source_url content exceeds the size limit","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"source_url could not be fetched","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"504":{"description":"source_url fetch timed out","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["upload_id"],"description":"Publish a completed staged upload session.","properties":{"upload_id":{"type":"string","example":"upl_01hzz00000000000000000"},"options":{"type":"object","description":"Drop settings (dropthis-owned serving behavior) applied at publish","additionalProperties":false,"properties":{"title":{"type":"string","default":"Untitled","example":"Summer Launch"},"visibility":{"type":"string","default":"public","enum":["public","unlisted"]},"workspace":{"type":"string","nullable":true,"example":"ws_team000000000000000001","description":"Publish into this workspace instead of the caller's active workspace (session/at_ principals only — the caller must be a member; a non-member target is 404). API keys (sk_) are bound to one workspace at mint and reject this field with 400 workspace_selector_not_allowed. On the upload_id path the workspace is taken from the upload; a value here that disagrees is 409 workspace_mismatch. Omitted: the active workspace."},"password":{"type":"string","nullable":true,"description":"Password required to view the drop. Available on Pro; below-Pro returns 403 feature_not_in_plan. Clear with null."},"noindex":{"type":"boolean","nullable":true,"description":"Prevent search indexing"},"expires_at":{"type":"string","format":"date-time","nullable":true},"domain":{"type":"string","nullable":true,"example":"drops.example.com","description":"Hostname of a custom domain connected to this account (must be live — see /v1/domains), or the literal 'shared' to publish to the shared pool even when the account has a default domain. Path-mode domains serve the drop at https://{domain}/{slug}/; dedicated domains serve it at the root and conflict (409) once occupied. Omitted: the account's default path domain if one exists, else the shared pool."},"slug":{"type":"string","nullable":true,"example":"summer-sale","description":"Vanity slug — only valid when the target is a path-mode custom domain. 1-63 lowercase letters/digits/hyphens (no leading/trailing/double hyphen). Taken slugs are auto-suffixed and the response notes it; omitted: a random slug. The shared pool is always random (422 if set)."}}},"metadata":{"type":"object","description":"Custom user or agent metadata stored with the drop","additionalProperties":true,"example":{"prompt_id":"msg_123","campaign":"summer-launch"}}}},{"type":"object","required":["source_url"],"description":"Publish by fetching a public URL server-side (SSRF-protected, single URL).","properties":{"source_url":{"type":"string","format":"uri","example":"https://example.com/report.html"},"options":{"type":"object","description":"Drop settings (dropthis-owned serving behavior) applied at publish","additionalProperties":false,"properties":{"title":{"type":"string","default":"Untitled","example":"Summer Launch"},"visibility":{"type":"string","default":"public","enum":["public","unlisted"]},"workspace":{"type":"string","nullable":true,"example":"ws_team000000000000000001","description":"Publish into this workspace instead of the caller's active workspace (session/at_ principals only — the caller must be a member; a non-member target is 404). API keys (sk_) are bound to one workspace at mint and reject this field with 400 workspace_selector_not_allowed. On the upload_id path the workspace is taken from the upload; a value here that disagrees is 409 workspace_mismatch. Omitted: the active workspace."},"password":{"type":"string","nullable":true,"description":"Password required to view the drop. Available on Pro; below-Pro returns 403 feature_not_in_plan. Clear with null."},"noindex":{"type":"boolean","nullable":true,"description":"Prevent search indexing"},"expires_at":{"type":"string","format":"date-time","nullable":true},"domain":{"type":"string","nullable":true,"example":"drops.example.com","description":"Hostname of a custom domain connected to this account (must be live — see /v1/domains), or the literal 'shared' to publish to the shared pool even when the account has a default domain. Path-mode domains serve the drop at https://{domain}/{slug}/; dedicated domains serve it at the root and conflict (409) once occupied. Omitted: the account's default path domain if one exists, else the shared pool."},"slug":{"type":"string","nullable":true,"example":"summer-sale","description":"Vanity slug — only valid when the target is a path-mode custom domain. 1-63 lowercase letters/digits/hyphens (no leading/trailing/double hyphen). Taken slugs are auto-suffixed and the response notes it; omitted: a random slug. The shared pool is always random (422 if set)."}}},"metadata":{"type":"object","description":"Custom user or agent metadata stored with the drop","additionalProperties":true,"example":{"prompt_id":"msg_123","campaign":"summer-launch"}}}}]},"examples":{"upload_id":{"summary":"Publish completed upload session","value":{"upload_id":"upl_01hzz00000000000000000","options":{"title":"Uploaded distribution"}}},"source_url":{"summary":"Publish from a URL","value":{"source_url":"https://example.com/report.html","options":{"title":"Imported"}}}}}},"required":true}},"get":{"tags":["Drops"],"summary":"List drops","description":"Lists drops owned by the authenticated account using cursor pagination. Pass ?domain=<hostname> to filter to drops mounted on one of your custom domains (owner-scoped; unknown hostnames return an empty list). To resolve a public URL or slug back to a drop, use POST /v1/drops/resolve.","operationId":"list_drops","security":[{"HTTPBearer":[]}],"parameters":[{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to drops mounted on this custom domain hostname (owner-scoped). The recovery path when an agent loses a drop id: list what a domain serves. Unknown hostnames return an empty list.","title":"Domain"},"description":"Filter to drops mounted on this custom domain hostname (owner-scoped). The recovery path when an agent loses a drop id: list what a domain serves. Unknown hostnames return an empty list."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque cursor from the previous page of results","title":"Cursor"},"description":"Opaque cursor from the previous page of results"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of results to return","default":20,"title":"Limit"},"description":"Maximum number of results to return"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDropsResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/resolve":{"post":{"tags":["Drops"],"summary":"Resolve a locator to a drop","description":"Resolves a drop locator — a drop id (drop_…), a bare slug, or a full public URL (shared-pool or custom-domain) — back to the owning drop. Owner-scoped: returns {data: <drop>} on a hit or {data: null} on any miss (unknown, foreign, or unparseable target — never a 404, never an existence leak). Persist the returned drop_… id; URLs and slugs are locators, not identifiers, and can drift.","operationId":"resolve_drop","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/drops/{drop_id}":{"get":{"tags":["Drops"],"summary":"Get a drop","description":"Returns owner-visible metadata for one drop. Drops owned by another account return 404.","operationId":"get_drop","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Drops"],"summary":"Update drop settings","description":"Updates drop settings (title, visibility, password, noindex, expiry) and metadata without creating a new deployment. The drop's content and URL are unchanged. Content updates use POST /v1/drops/{drop_id}/deployments.","operationId":"update_drop","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"If-Revision","in":"header","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"If-Revision"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Revision conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"description":"Update drop settings and metadata. Content updates use POST /v1/drops/{drop_id}/deployments.","properties":{"options":{"type":"object","description":"Drop settings to update","additionalProperties":false,"properties":{"title":{"type":"string","maxLength":500,"nullable":true},"visibility":{"type":"string","enum":["public","unlisted"],"nullable":true},"password":{"type":"string","nullable":true,"description":"Password required to view the drop. Available on Pro; below-Pro returns 403 feature_not_in_plan. Clear with null."},"noindex":{"type":"boolean","nullable":true},"expires_at":{"type":"string","format":"date-time","nullable":true},"domain":{"type":"string","nullable":true,"example":"drops.example.com","description":"Move the drop to this custom domain (re-mount, ADR 0056): the old URL 404s immediately, its slug is freed, no redirects. null moves the drop back to the shared pool."},"slug":{"type":"string","nullable":true,"example":"summer-sale","description":"Rename the drop's vanity slug on its path-mode custom domain. Explicit renames never auto-suffix: a taken slug is a 409."}}},"metadata":{"type":"object","description":"Custom user or agent metadata stored with the drop","additionalProperties":true,"example":{"prompt_id":"msg_123","campaign":"summer-launch"}}}},"examples":{"settings":{"summary":"Update title and visibility","value":{"options":{"title":"Updated Launch","visibility":"unlisted"}}}}}},"required":true}},"delete":{"tags":["Drops"],"summary":"Delete a drop","description":"Soft-deletes a drop and removes its published edge content.","operationId":"delete_drop","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/analytics":{"get":{"tags":["Drops"],"summary":"Get a drop's view analytics","description":"Approximate view analytics for one drop (F5). Gated by the `analytics` capability: Free returns 403 `feature_not_in_plan` (needs Keep); `view_count` plans (Keep) return `views` only; `full` plans (Pro+) also return `by_country` / `by_referer` / `daily`. Counts are best-effort (captured fire-and-forget at the edge) — an approximate signal, not exact human visits. Drops owned by another account return 404.","operationId":"get_drop_analytics","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropAnalyticsResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/deployments":{"post":{"tags":["Drops"],"summary":"Update a drop's content","description":"Updates a drop's content by creating a new immutable deployment and making it current once publish succeeds. The drop's URL and settings are unchanged. Returns 200 with the updated parent drop (DropResponse), not the deployment record — the drop carries the URL and revision agents need next.","operationId":"create_drop_deployment","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"If-Revision","in":"header","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"If-Revision"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Revision conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"source_url content exceeds the size limit","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"source_url could not be fetched","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"504":{"description":"source_url fetch timed out","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"requestBody":{"content":{"application/json":{"schema":{"description":"Updates a drop's content only: a deployment ships a new content version and keeps the drop's URL and settings. By default this is a logical partial update (mode 'patch') — provided files upsert by path and the rest are carried forward. Change settings (title, visibility, password, noindex, expiry, metadata) with PATCH /v1/drops/{drop_id} instead — sending them here is rejected.","oneOf":[{"type":"object","required":["upload_id"],"additionalProperties":false,"description":"Deploy a completed staged upload session as a new content version.","properties":{"upload_id":{"type":"string","example":"upl_01hzz00000000000000000"},"mode":{"type":"string","enum":["patch","replace"],"default":"patch","description":"Logical update mode. 'patch' (default): provided files upsert by path and any current file you do not mention is carried forward — editing one file no longer drops the rest. 'replace': the provided files become the entire new content set."},"delete_paths":{"type":"array","items":{"type":"string"},"default":[],"description":"Logical file paths to remove (patch mode only). Deleting a path that does not exist in the current deployment is a 422; a path both provided and deleted is a 422; delete_paths with mode 'replace' is a 422 (omission already deletes there).","example":["old/legacy.html"]}}},{"type":"object","required":["source_url"],"additionalProperties":false,"description":"Deploy by fetching a public URL server-side (SSRF-protected, single URL).","properties":{"source_url":{"type":"string","format":"uri","example":"https://example.com/report.html"},"mode":{"type":"string","enum":["patch","replace"],"default":"patch","description":"Logical update mode. 'patch' (default): provided files upsert by path and any current file you do not mention is carried forward — editing one file no longer drops the rest. 'replace': the provided files become the entire new content set."},"delete_paths":{"type":"array","items":{"type":"string"},"default":[],"description":"Logical file paths to remove (patch mode only). Deleting a path that does not exist in the current deployment is a 422; a path both provided and deleted is a 422; delete_paths with mode 'replace' is a 422 (omission already deletes there).","example":["old/legacy.html"]}}},{"type":"object","required":["delete_paths"],"additionalProperties":false,"description":"Delete-only patch: remove named files with no content input. Mints a new deployment from (current files − delete_paths), carrying the rest forward.","properties":{"delete_paths":{"type":"array","items":{"type":"string"},"default":[],"description":"Logical file paths to remove (patch mode only). Deleting a path that does not exist in the current deployment is a 422; a path both provided and deleted is a 422; delete_paths with mode 'replace' is a 422 (omission already deletes there).","example":["old/legacy.html"],"minItems":1},"mode":{"type":"string","enum":["patch"],"default":"patch"}}}]},"examples":{"upload_id":{"summary":"Patch (default): upsert these files, carry the rest forward","value":{"upload_id":"upl_01hzz00000000000000000"}},"replace":{"summary":"Replace the entire content set","value":{"upload_id":"upl_01hzz00000000000000000","mode":"replace"}},"source_url":{"summary":"Deploy from a URL","value":{"source_url":"https://example.com/report.html"}},"delete_paths":{"summary":"Delete files only (no content input)","value":{"delete_paths":["old/legacy.html"]}}}}},"required":true}},"get":{"tags":["Drops"],"summary":"List drop deployments","description":"List immutable content deployments for one drop using cursor pagination.","operationId":"list_drop_deployments","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque cursor from the previous page of results","title":"Cursor"},"description":"Opaque cursor from the previous page of results"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of results to return","default":20,"title":"Limit"},"description":"Maximum number of results to return"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDropDeploymentsResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/deployments/{deployment_id}/restore":{"post":{"tags":["Drops"],"summary":"Restore a prior deployment","description":"Roll a drop back to a prior deployment's content (Pro+ version-history feature). Creates a NEW deployment whose content is copied from the chosen prior deployment and makes it current — the drop's URL and settings are unchanged, only the content is restored. Listing and reading deployments stays open (ungated); only this restore write requires the version_history capability. Restoring the deployment that is already current is a 200 no-op. Returns 200 with the updated parent drop (DropResponse).","operationId":"restore_drop_deployment","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"deployment_id","in":"path","required":true,"schema":{"type":"string","description":"Stable deployment identifier","title":"Deployment Id"},"description":"Stable deployment identifier"},{"name":"If-Revision","in":"header","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"If-Revision"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response.","title":"Idempotency-Key"},"description":"Unique string (v4 UUID recommended). Same key within 24h returns cached response."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan quota exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Revision conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"source_url could not be fetched","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"504":{"description":"source_url fetch timed out","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/deployments/{deployment_id}":{"get":{"tags":["Drops"],"summary":"Get a drop deployment","description":"Return metadata for one immutable content deployment belonging to a drop.","operationId":"get_drop_deployment","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"deployment_id","in":"path","required":true,"schema":{"type":"string","description":"Stable deployment identifier","title":"Deployment Id"},"description":"Stable deployment identifier"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropDeploymentResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/content":{"get":{"tags":["Drops"],"summary":"Read back current drop content","description":"Owner-only read-back of the drop's CURRENT deployment. Returns a JSON manifest of the deployment's files (paths, sizes, content types, entry) by default; pass ?path=<file> to download one file's exact stored bytes with its stored content type. Works regardless of any viewer password. For a historical content version use GET /v1/drops/{drop_id}/deployments/{deployment_id}/content.","operationId":"get_drop_content","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"path","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Return this file's raw stored bytes (with its stored content type) instead of the JSON manifest. Must be one of the manifest's files[].path values.","title":"Path"},"description":"Return this file's raw stored bytes (with its stored content type) instead of the JSON manifest. Must be one of the manifest's files[].path values."}],"responses":{"200":{"description":"JSON manifest of the deployment's files, or the raw bytes of one file when ?path= is given","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentContentManifest"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop, deployment, or file path not found (deleted drops have no readable content)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/drops/{drop_id}/deployments/{deployment_id}/content":{"get":{"tags":["Drops"],"summary":"Read back a deployment's content","description":"Owner-only read-back of one immutable deployment's content. Returns a JSON manifest of its files (paths, sizes, content types, entry) by default; pass ?path=<file> to download one file's exact stored bytes with its stored content type. Works for SUPERSEDED deployments too: downloading an old version's files and republishing them with POST /v1/drops/{drop_id}/deployments is the rollback path (decision 0052).","operationId":"get_drop_deployment_content","security":[{"HTTPBearer":[]}],"parameters":[{"name":"drop_id","in":"path","required":true,"schema":{"type":"string","description":"Stable drop identifier","title":"Drop Id"},"description":"Stable drop identifier"},{"name":"deployment_id","in":"path","required":true,"schema":{"type":"string","description":"Stable deployment identifier","title":"Deployment Id"},"description":"Stable deployment identifier"},{"name":"path","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Return this file's raw stored bytes (with its stored content type) instead of the JSON manifest. Must be one of the manifest's files[].path values.","title":"Path"},"description":"Return this file's raw stored bytes (with its stored content type) instead of the JSON manifest. Must be one of the manifest's files[].path values."}],"responses":{"200":{"description":"JSON manifest of the deployment's files, or the raw bytes of one file when ?path= is given","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentContentManifest"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Drop, deployment, or file path not found (deleted drops have no readable content)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account":{"get":{"tags":["Account"],"summary":"Get account","description":"Return profile, status, plan, the full `entitlements` matrix (capabilities + numeric limits + per-feature required_plan), current usage, and an upgrade URL. Use `entitlements` to pre-check a feature gate or size a publish before uploading; point a human at `upgrade_url` when they hit a gate.","operationId":"get_account_v1_account_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"tags":["Account"],"summary":"Delete account","description":"Soft-delete the authenticated account and disable future access (ADR 0066/0068). Requires the `account:delete` scope — never bundled into any login, so an everyday publish/team/service credential is rejected (403 insufficient_scope); account deletion is the highest-assurance act and is granted only on explicit, named request. For a session principal a fresh step-up is additionally required. Account deletion tears down ONLY the account's personal workspace (its drops + edge meta); team workspaces survive with their other owners — the account's memberships are removed and all its sk_ keys revoked. Deletion is blocked (409 sole_owner_of_team) if the account is the sole owner of any team workspace.","operationId":"delete_account_v1_account_delete","responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the `account:delete` scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The account is the sole owner of one or more team workspaces","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]},"patch":{"tags":["Account"],"summary":"Update account","description":"Update mutable profile fields for the authenticated account.","operationId":"update_account_v1_account_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAccountRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/account/active-workspace":{"put":{"tags":["Account"],"summary":"Set active workspace","description":"Select the workspace that subsequent requests act within (ADR 0066, Task 1.5). Pass `{\"workspace\": null}` to reset to the personal workspace. Accepts any authenticated principal: session and oauth_grant (delegated) credentials may switch; a SERVICE sk_ key is rejected with 400 workspace_pinned — it is pinned at mint. For delegated (oauth_grant) credentials the switch is stored on the key; for session/at_ principals it is stored on the account. The caller must be a member of the target workspace (else 404). No step-up required (non-destructive).","operationId":"set_active_workspace_v1_account_active_workspace_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetActiveWorkspaceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveWorkspaceResponse"}}}},"400":{"description":"SERVICE sk_ key cannot switch workspaces (workspace_pinned)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Caller is not a member of the target workspace","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/workspaces":{"get":{"tags":["Workspaces"],"summary":"List workspaces","description":"List every workspace the caller is a member of, including the personal workspace, each annotated with the caller's role and whether it is the active workspace. Reachable by console session (session) and delegated sk_ keys (oauth_grant). Service sk_ keys (api_key) are rejected — they are pinned to one workspace.","operationId":"list_workspaces_v1_workspaces_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceListResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Workspaces"],"summary":"Create a team workspace","description":"Create a new team workspace with the calling account as its sole owner. The slug is derived from the name when omitted; a clash on an explicit slug returns 409.","operationId":"create_workspace_v1_workspaces_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkspaceRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/workspaces/{workspace_id}":{"get":{"tags":["Workspaces"],"summary":"Get a workspace","description":"Return a workspace the caller belongs to, with the caller's role. 404 if not a member.","operationId":"get_workspace_v1_workspaces__workspace_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Workspaces"],"summary":"Rename a workspace","description":"Rename a team workspace's name and/or slug (owner or admin). The personal workspace is immutable (409). Requires a fresh step-up.","operationId":"rename_workspace_v1_workspaces__workspace_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameWorkspaceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Workspaces"],"summary":"Delete a workspace","description":"Delete a team workspace (owner only). The personal workspace cannot be deleted here (409). Requires a fresh step-up.","operationId":"delete_workspace_v1_workspaces__workspace_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/workspaces/{workspace_id}/members":{"get":{"tags":["Workspaces"],"summary":"List workspace members","description":"List every member of a workspace (any member may read). Each row carries the member's email, role, and whether it is the calling account. A member whose account was deleted renders with a null email.","operationId":"list_members_v1_workspaces__workspace_id__members_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberListResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/workspaces/{workspace_id}/members/{account_id}":{"patch":{"tags":["Workspaces"],"summary":"Change a member's role","description":"Change a member's role (owner or admin). Promoting to or demoting from owner requires the caller be an owner (403 requires_owner otherwise); the last owner cannot be demoted (409 last_owner). Requires a fresh step-up.","operationId":"change_member_role_v1_workspaces__workspace_id__members__account_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"},{"name":"account_id","in":"path","required":true,"schema":{"type":"string","description":"Member account identifier (acc_…)","title":"Account Id"},"description":"Member account identifier (acc_…)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeRoleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Workspaces"],"summary":"Remove a member (or leave)","description":"Remove a member, or leave the workspace (remove yourself). Removing an owner requires the caller be an owner (403 requires_owner); the last owner cannot be removed (409 last_owner). The removed member's workspace-bound API keys are revoked. Requires a fresh step-up.","operationId":"remove_member_v1_workspaces__workspace_id__members__account_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"},{"name":"account_id","in":"path","required":true,"schema":{"type":"string","description":"Member account identifier (acc_…)","title":"Account Id"},"description":"Member account identifier (acc_…)"}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/workspaces/{workspace_id}/invitations":{"post":{"tags":["Workspaces"],"summary":"Invite a user to a workspace","description":"Send an invitation email to a new member (owner or admin). Re-inviting an already-pending email replaces the prior invite (race-safe). Inviting an existing member returns 409 already_member. Subject to per-(workspace, email) rate limits.","operationId":"invite_member_v1_workspaces__workspace_id__invitations_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"tags":["Workspaces"],"summary":"List workspace invitations","description":"List all invitations for a workspace (owner or admin only).","operationId":"list_workspace_invitations_v1_workspaces__workspace_id__invitations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationListResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/workspaces/{workspace_id}/invitations/{invitation_id}":{"delete":{"tags":["Workspaces"],"summary":"Revoke an invitation","description":"Revoke a pending invitation (owner or admin). Idempotent — revoking a non-existent or already-revoked invitation still returns 204. Requires a fresh step-up.","operationId":"revoke_invitation_v1_workspaces__workspace_id__invitations__invitation_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","description":"Invitation identifier (inv_…)","title":"Invitation Id"},"description":"Invitation identifier (inv_…)"}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/workspaces/{workspace_id}/invitations/{invitation_id}/resend":{"post":{"tags":["Workspaces"],"summary":"Resend an invitation","description":"Rotate the invite token and resend the email (owner or admin). Subject to a per-invitation cooldown and the per-(workspace, email) rate limit. Returns 404 if the invitation is not pending or does not belong to this workspace.","operationId":"resend_invitation_v1_workspaces__workspace_id__invitations__invitation_id__resend_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","description":"Workspace identifier (ws_…)","title":"Workspace Id"},"description":"Workspace identifier (ws_…)"},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","description":"Invitation identifier (inv_…)","title":"Invitation Id"},"description":"Invitation identifier (inv_…)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"401":{"description":"Missing or invalid authentication, or step-up required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Workspace not found or caller lacks the required role","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Slug already taken, or the personal workspace cannot be renamed/deleted","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/invitations":{"get":{"tags":["Invitations"],"summary":"List caller's pending invitations","description":"List all pending invitations addressed to the calling account's email. Console-session only.","operationId":"list_caller_invitations_v1_invitations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationListResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Invitation not found or already accepted/revoked","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/invitations/accept":{"post":{"tags":["Invitations"],"summary":"Accept a workspace invitation","description":"Accept a pending invitation by presenting the raw single-use token from the invite email. On success, creates the membership and sets the caller's active workspace to the joined workspace. Returns the joined workspace with the granted role. Console-session only.","operationId":"accept_invitation_v1_invitations_accept_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Invitation not found or already accepted/revoked","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/invitations/accept-by-id":{"post":{"tags":["Invitations"],"summary":"Accept a workspace invitation by id","description":"Accept a pending invitation by its id, once authenticated as the invited email — the agent path, no raw token needed (list your invitations, accept one by id). A wrong-email, non-pending, or expired id returns a uniform 404 (no existence leak). On success, creates the membership and switches the caller's active workspace to the joined workspace.","operationId":"accept_invitation_by_id_v1_invitations_accept_by_id_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptByIdRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Credential lacks the required scope (insufficient_scope)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Invitation not found or already accepted/revoked","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/domains":{"get":{"tags":["Domains"],"summary":"List custom domains","description":"Lists all custom domains connected to the authenticated account.","operationId":"list_domains","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainListResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Domains"],"summary":"Connect a custom domain","description":"Registers a hostname with dropthis via Cloudflare for SaaS, returning DNS instructions. Idempotent on (account, hostname): re-connect returns the existing domain with 200. mode is immutable — delete and reconnect to change it.","operationId":"connect_domain","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectDomainRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Plan does not allow this operation: feature_not_in_plan (custom domains require Pro) or quota_exceeded (custom-hostname cap reached)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Hostname already connected to a different account","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload or validation error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/domains/{id_or_hostname}":{"get":{"tags":["Domains"],"summary":"Get a custom domain","description":"Returns a custom domain by its dom_… id or hostname.","operationId":"get_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id_or_hostname","in":"path","required":true,"schema":{"type":"string","description":"Domain identifier (dom_…) or hostname","title":"Id Or Hostname"},"description":"Domain identifier (dom_…) or hostname"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Domain not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Domains"],"summary":"Update a custom domain","description":"Updates allowed domain fields: drop_id (dedicated only — repoints the mounted drop) and default (path + live only — sets the account publish default). mode is immutable — delete and reconnect to change it.","operationId":"update_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id_or_hostname","in":"path","required":true,"schema":{"type":"string","description":"Domain identifier (dom_…) or hostname","title":"Id Or Hostname"},"description":"Domain identifier (dom_…) or hostname"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Domain not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload or validation error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Domains"],"summary":"Delete a custom domain","description":"Disconnects a custom domain: stops serving all drops on it, removes the Cloudflare custom hostname, and deletes the row. WARNING: if your DNS CNAME still points at edge.dropthis.app after deletion, the hostname can be re-connected by another account. Remove your DNS record immediately after deleting.","operationId":"delete_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id_or_hostname","in":"path","required":true,"schema":{"type":"string","description":"Domain identifier (dom_…) or hostname","title":"Id Or Hostname"},"description":"Domain identifier (dom_…) or hostname"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainDeletedResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Domain not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}},"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/domains/{id_or_hostname}/verify":{"post":{"tags":["Domains"],"summary":"Verify a custom domain","description":"Re-checks DNS (DoH) and Cloudflare validation, advancing the domain status machine. Always re-callable — the recovery path for CAA/rate-limit/validation failures. Returns the updated domain with expected-vs-observed DNS diagnostics.","operationId":"verify_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id_or_hostname","in":"path","required":true,"schema":{"type":"string","description":"Domain identifier (dom_…) or hostname","title":"Id Or Hostname"},"description":"Domain identifier (dom_…) or hostname"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainResponse"}}}},"401":{"description":"Missing or invalid authentication","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Domain not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request payload or validation error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/edge/unlock":{"post":{"tags":["Edge"],"summary":"Verify drop password (viewer unlock)","description":"Called by the CF Worker viewer when a visitor submits a password for a password-protected drop. Returns the drop's access_revision on success so the viewer can bind an unlock cookie to the correct revision. Unknown drops, unprotected drops, and wrong passwords all return the same 401 (no oracle). Rate-limited per (drop_id, client IP): 10 attempts / 5 minutes.","operationId":"unlock_v1_edge_unlock_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockRequest"}}},"required":true},"responses":{"200":{"description":"Password correct — unlock cookie may be issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockResponse"}}}},"401":{"description":"Invalid drop or password (no oracle)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many unlock attempts for this drop / IP pair","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid request body","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AcceptByIdRequest":{"properties":{"invitation_id":{"type":"string","minLength":1,"title":"Invitation Id","description":"The invitation id (inv_…) to accept, once authenticated as the invited email."}},"additionalProperties":false,"type":"object","required":["invitation_id"],"title":"AcceptByIdRequest"},"AcceptInvitationRequest":{"properties":{"token":{"type":"string","minLength":1,"title":"Token","description":"The raw single-use token from the invite email link"}},"additionalProperties":false,"type":"object","required":["token"],"title":"AcceptInvitationRequest"},"AccountResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stable account identifier","examples":["acc_01hzz00000000000000000"]},"email":{"type":"string","title":"Email","description":"Primary email address for the account","examples":["ada@example.com"]},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Optional public display name","examples":["Ada Lovelace"]},"plan":{"type":"string","title":"Plan","description":"Active billing plan tier","examples":["free"]},"status":{"type":"string","title":"Status","description":"Account lifecycle status","examples":["active"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Account creation timestamp","examples":["2026-05-23T12:00:00Z"]},"entitlements":{"$ref":"#/components/schemas/Entitlements","description":"The full capability matrix + numeric limits for the active plan — the single read to pre-check a feature gate or size a publish before attempting it."},"usage":{"$ref":"#/components/schemas/AccountUsage","description":"Current usage counters for the account"},"workspace":{"$ref":"#/components/schemas/AccountWorkspace","description":"The workspace this principal acts within (ADR 0066). For an sk_ API key this is the workspace the key is bound to — every drop, domain, and upload lives here. `kind` is `team` when you share the workspace with other members; `role` is your role in it."},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url","description":"URL of the pricing page — present on every plan with a higher tier to upgrade to; null on the top (Business) plan","examples":["https://dropthis.app/pricing"]}},"type":"object","required":["id","email","plan","status","created_at","entitlements","usage","workspace","upgrade_url"],"title":"AccountResponse"},"AccountUsage":{"properties":{"storage_used_bytes":{"type":"integer","title":"Storage Used Bytes","description":"Bytes used by active (non-expired) drops","examples":[1048576]},"custom_domains_used":{"type":"integer","title":"Custom Domains Used","description":"Custom hostnames currently connected","examples":[0]},"seats_used":{"type":"integer","title":"Seats Used","description":"Members currently in the workspace (owner included)","examples":[1]}},"type":"object","required":["storage_used_bytes","custom_domains_used","seats_used"],"title":"AccountUsage"},"AccountWorkspace":{"properties":{"id":{"type":"string","title":"Id","description":"The workspace this principal acts within","examples":["ws_01hzz00000000000000000"]},"name":{"type":"string","title":"Name","description":"Workspace name","examples":["Byrokko"]},"slug":{"type":"string","title":"Slug","description":"URL-safe workspace slug","examples":["byrokko"]},"kind":{"type":"string","title":"Kind","description":"Workspace kind","examples":["personal","team"]},"role":{"type":"string","title":"Role","description":"The calling account's role in this workspace","examples":["owner","admin","member"]}},"type":"object","required":["id","name","slug","kind","role"],"title":"AccountWorkspace"},"Action":{"properties":{"code":{"type":"string","title":"Code","description":"Machine-readable action code","examples":["verify_email_otp"]},"kind":{"type":"string","title":"Kind","description":"Action type: 'api' (call an endpoint), 'external' (upload to signed URL), 'human' (user action)","examples":["api"]},"method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Method","description":"HTTP method for api actions","examples":["POST"]},"endpoint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint","description":"Relative API path for api actions","examples":["/v1/auth/email/verify"]},"message":{"type":"string","title":"Message","description":"Plain English explanation of the action","examples":["Verify the 6-digit code sent to your email."]}},"type":"object","required":["code","kind","message"],"title":"Action"},"ActionResolve":{"properties":{"method":{"type":"string","title":"Method","description":"HTTP method","examples":["GET"]},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Absolute URL for human actions","examples":["https://dropthis.app/pricing"]},"endpoint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint","description":"Relative API path for agent-resolvable actions","examples":["/v1/drops"]}},"type":"object","required":["method"],"title":"ActionResolve"},"ActiveWorkspaceResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Resolved active workspace identifier","examples":["ws_01hzz00000000000000000"]},"name":{"type":"string","title":"Name","description":"Workspace name","examples":["Acme"]},"slug":{"type":"string","title":"Slug","description":"Workspace slug","examples":["acme"]},"kind":{"type":"string","title":"Kind","description":"Workspace kind","examples":["personal","team"]},"plan":{"type":"string","title":"Plan","description":"Active billing plan tier of the workspace","examples":["free"]},"role":{"type":"string","title":"Role","description":"The calling account's role in the resolved workspace","examples":["owner","member"]}},"type":"object","required":["id","name","slug","kind","plan","role"],"title":"ActiveWorkspaceResponse"},"AnalyticsBreakdownItem":{"properties":{"key":{"type":"string","title":"Key","description":"The dimension value (country code or referer host)","examples":["US"]},"views":{"type":"integer","title":"Views","description":"Views attributed to this value","examples":[42]}},"type":"object","required":["key","views"],"title":"AnalyticsBreakdownItem"},"AnalyticsDailyPoint":{"properties":{"date":{"type":"string","title":"Date","description":"UTC day (YYYY-MM-DD)","examples":["2026-06-20"]},"views":{"type":"integer","title":"Views","description":"Views on that day","examples":[7]}},"type":"object","required":["date","views"],"title":"AnalyticsDailyPoint"},"ApiKeyCreatedResponse":{"properties":{"object":{"type":"string","const":"api_key","title":"Object","default":"api_key"},"id":{"type":"string","title":"Id","description":"Stable API key identifier","examples":["ak_01hzz00000000000000000"]},"key_last4":{"type":"string","title":"Key Last4","description":"Last four characters of the API key secret","examples":["wxyz"]},"label":{"type":"string","title":"Label","description":"Human-readable API key label","examples":["CLI key"]},"key_type":{"$ref":"#/components/schemas/KeyType","description":"How the credential resolves its workspace. `delegated` keys are account-scoped and switchable (CLI login, MCP OAuth grant, console session); `service` keys are pinned to one workspace for CI/automation. The console groups these separately (Connected apps vs API keys).","examples":["delegated"]},"app_name":{"type":"string","title":"App Name","description":"Human-facing name for the credential: the connected client name (e.g. Claude Desktop) for MCP-grant labels, or the user's verbatim label for CLI logins and CI keys.","examples":["Production deploy key"]},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","description":"Permissions this key grants, as a stable UI-facing descriptor.","examples":[["drops:read","drops:write"]]},"revoke_impact":{"$ref":"#/components/schemas/RevokeImpact","description":"What breaks if this key is revoked: `disconnects_app` for a delegated login/connected app, `breaks_automation` for a service CI/automation key. The console turns this into a 'what happens if you revoke this' confirmation.","examples":["breaks_automation"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"API key creation timestamp","examples":["2026-05-23T12:00:00Z"]},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"When the key last authenticated a request, or null if it has never been used.","examples":["2026-06-16T10:00:00Z"]},"key":{"type":"string","title":"Key","description":"Raw API key secret, shown only once","examples":["sk_live_abc123"]},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Id","description":"Account identifier associated with the key when returned during bootstrap","examples":["acc_01hzz00000000000000000"]},"is_new_account":{"type":"boolean","title":"Is New Account","description":"Whether creating the key also created a new account","default":false,"examples":[false]}},"type":"object","required":["id","key_last4","label","key_type","app_name","scopes","revoke_impact","created_at","key"],"title":"ApiKeyCreatedResponse"},"ApiKeyListResponse":{"properties":{"object":{"type":"string","const":"list","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/ApiKeyResponse"},"type":"array","title":"Data","description":"Active API keys for the authenticated account"}},"type":"object","required":["data"],"title":"ApiKeyListResponse"},"ApiKeyResponse":{"properties":{"object":{"type":"string","const":"api_key","title":"Object","default":"api_key"},"id":{"type":"string","title":"Id","description":"Stable API key identifier","examples":["ak_01hzz00000000000000000"]},"key_last4":{"type":"string","title":"Key Last4","description":"Last four characters of the API key secret","examples":["wxyz"]},"label":{"type":"string","title":"Label","description":"Human-readable API key label","examples":["CLI key"]},"key_type":{"$ref":"#/components/schemas/KeyType","description":"How the credential resolves its workspace. `delegated` keys are account-scoped and switchable (CLI login, MCP OAuth grant, console session); `service` keys are pinned to one workspace for CI/automation. The console groups these separately (Connected apps vs API keys).","examples":["delegated"]},"app_name":{"type":"string","title":"App Name","description":"Human-facing name for the credential: the connected client name (e.g. Claude Desktop) for MCP-grant labels, or the user's verbatim label for CLI logins and CI keys.","examples":["Production deploy key"]},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","description":"Permissions this key grants, as a stable UI-facing descriptor.","examples":[["drops:read","drops:write"]]},"revoke_impact":{"$ref":"#/components/schemas/RevokeImpact","description":"What breaks if this key is revoked: `disconnects_app` for a delegated login/connected app, `breaks_automation` for a service CI/automation key. The console turns this into a 'what happens if you revoke this' confirmation.","examples":["breaks_automation"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"API key creation timestamp","examples":["2026-05-23T12:00:00Z"]},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"When the key last authenticated a request, or null if it has never been used.","examples":["2026-06-16T10:00:00Z"]}},"type":"object","required":["id","key_last4","label","key_type","app_name","scopes","revoke_impact","created_at"],"title":"ApiKeyResponse"},"ChangeRoleRequest":{"properties":{"role":{"$ref":"#/components/schemas/WorkspaceRole","description":"The member's new role","examples":["admin","member","owner"]}},"additionalProperties":false,"type":"object","required":["role"],"title":"ChangeRoleRequest"},"ConnectDomainRequest":{"properties":{"hostname":{"type":"string","title":"Hostname","description":"Hostname to connect (canonicalized server-side)","examples":["drops.nokia.com"]},"mode":{"type":"string","enum":["path","dedicated"],"title":"Mode","description":"Mount mode: 'path' (many drops at hostname/{slug}/) or 'dedicated' (one drop at hostname/)","examples":["path"]}},"additionalProperties":false,"type":"object","required":["hostname","mode"],"title":"ConnectDomainRequest","description":"POST /v1/domains body. Declaratively bound (FastAPI), matching\nUpdateDomainRequest: missing/extra keys and an invalid ``mode`` are 422 at\nthe schema boundary — no hand-rolled body parsing in the route."},"CreateApiKeyRequest":{"properties":{"label":{"type":"string","maxLength":255,"minLength":1,"title":"Label","description":"Human-readable API key label","examples":["Production deploy key"]},"key_type":{"$ref":"#/components/schemas/KeyType","description":"Credential mode. Defaults to `delegated`: the account-scoped, switchable login credential (CLI login, MCP OAuth grant, console session) with its own generous cap, homed to the account's personal workspace. `service` is the explicit CI/automation opt-in: pinned to the minting workspace, no switch, and sharing the small per-account quota. A pinned service key may not mint a delegated credential (escalation guard).","default":"delegated","examples":["delegated"]},"allowed_workspace_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Workspace Ids","description":"DELEGATED keys only: the workspaces this account-scoped credential may reach. Null (the default) means all of the account's current memberships; a provided list must be a subset of those memberships. Ignored for `service` keys.","examples":[["ws_personal","ws_team"]]},"workspace":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace","description":"SERVICE keys only — pin the CI key to this workspace, a slug or id the account is a member of. Omit to pin to the caller's current active workspace (default). Ignored for delegated keys, which always home to personal.","examples":["my-team"]},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"Request the credential's capability scopes (ADR 0068). Each entry is a bundle name (`publish`, `team`, `team-admin`) or a fine-grained scope (`members:admin`). The minted key gets the requested set INTERSECTED with your own scopes (downscope-only — you can never grant beyond yourself). Omit for the default `publish` bundle. Use `['team']` to mint a credential that can create + manage teams.","examples":[["team"],["publish"],["drops:read"]]}},"type":"object","required":["label"],"title":"CreateApiKeyRequest"},"CreateUploadFileRequest":{"properties":{"path":{"type":"string","title":"Path","description":"Normalized POSIX path the file will have inside the staged artifact. Duplicate paths are rejected.","examples":["index.html"]},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Public http(s) URL the server fetches into staging on behalf of the client. When set, content_type and size_bytes become optional (they may be discovered from the response). When absent, content_type and size_bytes are required.","examples":["https://cdn.example.com/assets/logo.png"]},"content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Type","description":"MIME type the client must use when uploading bytes to the returned signed target.","examples":["text/html"]},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes","description":"Declared byte size for this file; completion verifies the stored object size matches this value. On a `source_url` fetch, a mismatch between this declared size and the fetched byte count is a hard, non-retryable per-file failure that fails the whole publish (atomic — no partial deploy).","examples":[204800]},"checksum_sha256":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checksum Sha256","description":"Optional lowercase hex SHA-256 checksum for the file bytes. When provided, the signed upload target requires the matching x-amz-checksum-sha256 header (included in the returned headers) and storage rejects mismatched bytes; completion re-verifies the stored checksum. On a `source_url` fetch, the server re-computes the SHA-256 of the fetched bytes and a mismatch is a hard, non-retryable per-file failure that fails the whole publish (atomic — no partial deploy).","examples":["3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"]},"transform":{"anyOf":[{"$ref":"#/components/schemas/TransformRequest"},{"type":"null"}],"description":"Optional server-side image transform applied to a `source_url` fetch before staging. Only valid with `source_url`. When set, `size_bytes` and `checksum_sha256` must be omitted — the stored object's size, content type, and checksum reflect the TRANSFORM OUTPUT and are computed server-side."}},"type":"object","required":["path"],"title":"CreateUploadFileRequest"},"CreateUploadSessionRequest":{"properties":{"schema_version":{"type":"integer","title":"Schema Version","description":"Upload manifest schema version. Version 1 is the only accepted value.","default":1,"examples":[1]},"files":{"items":{"$ref":"#/components/schemas/CreateUploadFileRequest"},"type":"array","title":"Files","description":"Manifest of files to upload before publishing. At least one file is required.","examples":[[{"content_type":"text/html","path":"index.html","size_bytes":204800},{"content_type":"application/javascript","path":"assets/app.js","size_bytes":12000}]]},"entry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry","description":"Path of the entry file to serve first when the staged artifact is published.","examples":["index.html"]},"workspace":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace","description":"Stage this upload into the given workspace instead of the caller's active workspace (session/at_ principals only — the caller must be a member; a non-member target is 404). API keys (sk_) are bound to one workspace at mint and reject this field with 400 workspace_selector_not_allowed. The upload's later complete/ingest/publish all resolve from this stored workspace, regardless of the caller's active workspace. Omitted: the active workspace.","examples":["ws_team000000000000000001"]}},"type":"object","required":["files"],"title":"CreateUploadSessionRequest"},"CreateUploadSessionResponse":{"properties":{"upload_id":{"type":"string","title":"Upload Id","description":"Stable upload session identifier to use for status, completion, cancellation, and publish calls.","examples":["upl_01hzz00000000000000000"]},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Time when the upload session stops accepting normal completion attempts.","examples":["2026-05-15T12:00:00Z"]},"files":{"items":{"$ref":"#/components/schemas/UploadTargetResponse"},"type":"array","title":"Files","description":"Per-file direct storage upload targets for the declared manifest.","examples":[[{"file_id":"file_000001","object_key":"staging/acc_01hzz00000000000000000/upl_01hzz00000000000000000/files/000001","path":"index.html","upload":{"expires_at":"2026-05-15T12:00:00Z","headers":{"content-type":"text/html"},"strategy":"single_put","url":"https://storage.example/upload/index.html"}}]]},"next_action":{"$ref":"#/components/schemas/Action","description":"Next step after creating the upload session","default":{"code":"upload_files","kind":"external","message":"Upload each file to its signed URL, then complete the session."}}},"type":"object","required":["upload_id","expires_at","files"],"title":"CreateUploadSessionResponse"},"CreateWorkspaceRequest":{"properties":{"name":{"type":"string","minLength":1,"title":"Name","description":"Human name for the workspace","examples":["Acme Corp"]},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug","description":"Optional URL-safe slug; derived from the name when omitted. Must be unique.","examples":["acme"]}},"additionalProperties":false,"type":"object","required":["name"],"title":"CreateWorkspaceRequest"},"DeploymentContentFile":{"properties":{"path":{"type":"string","title":"Path","description":"File path within the deployment, relative to its root","examples":["index.html"]},"content_type":{"type":"string","title":"Content Type","description":"Stored MIME type the file is served with","examples":["text/html"]},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Stored file size in bytes","examples":[1024]}},"type":"object","required":["path","content_type","size_bytes"],"title":"DeploymentContentFile"},"DeploymentContentManifest":{"properties":{"drop_id":{"type":"string","title":"Drop Id","description":"Parent drop identifier","examples":["drop_01hzz00000000000000000"]},"deployment_id":{"type":"string","title":"Deployment Id","description":"Deployment identifier","examples":["dep_01hzz00000000000000000"]},"revision":{"type":"integer","title":"Revision","description":"Content revision of this deployment","examples":[2]},"status":{"type":"string","title":"Status","description":"Deployment lifecycle status","examples":["published"]},"entry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry","description":"Entry path served at the drop root","examples":["index.html"]},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Total deployment size in bytes","examples":[12345]},"files":{"items":{"$ref":"#/components/schemas/DeploymentContentFile"},"type":"array","title":"Files","description":"Readable files in this deployment; pass files[].path as ?path= to download one"}},"type":"object","required":["drop_id","deployment_id","revision","status","size_bytes","files"],"title":"DeploymentContentManifest","description":"Manifest of one deployment's readable files (content read-back).\n\nFetch a single file's bytes with `?path=<files[].path>` on the same route."},"DnsRecord":{"properties":{"purpose":{"type":"string","title":"Purpose","description":"Purpose of this record","default":"routing","examples":["routing"]},"type":{"type":"string","title":"Type","description":"DNS record type","default":"CNAME","examples":["CNAME"]},"name":{"type":"string","title":"Name","description":"The DNS name to create the record for","examples":["drops.nokia.com"]},"value":{"type":"string","title":"Value","description":"The value the record must point at","examples":["edge.dropthis.app"]},"status":{"type":"string","title":"Status","description":"Current DNS status: 'missing' | 'ok' | 'mismatch'","examples":["missing"]},"observed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Observed","description":"What DoH currently resolves for this record (verify only)","examples":["edge.dropthis.app"]},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Specific guidance for fixing this record","examples":["Create a CNAME record for drops.nokia.com pointing at edge.dropthis.app at your DNS provider."]},"retry_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Retry After","description":"Seconds to wait before retrying verify while DNS/cert propagates","examples":[300]}},"type":"object","required":["name","value","status"],"title":"DnsRecord","description":"One DNS record instruction or diagnostic."},"DomainDeletedResponse":{"properties":{"object":{"type":"string","title":"Object","description":"Object type discriminator","default":"domain.deleted","examples":["domain.deleted"]},"id":{"type":"string","title":"Id","description":"Id of the deleted domain","examples":["dom_01hzz00000000000000000"]},"hostname":{"type":"string","title":"Hostname","description":"Hostname of the deleted domain","examples":["drops.nokia.com"]},"warning":{"type":"string","title":"Warning","description":"Dangling-CNAME risk: while the customer's CNAME still points at edge.dropthis.app, the hostname can be re-connected by another account.","examples":["Remove your DNS record for drops.nokia.com: while your CNAME still points at ..."]}},"type":"object","required":["id","hostname","warning"],"title":"DomainDeletedResponse","description":"Response body for a successful domain deletion.\n\nDELETE returns 200+body (not 204) because the ADR documents a dangling-CNAME\nwarning that must be surfaced to the caller. A 204 has no body so we use 200."},"DomainListResponse":{"properties":{"object":{"type":"string","title":"Object","description":"Object type discriminator","default":"domain.list","examples":["domain.list"]},"domains":{"items":{"$ref":"#/components/schemas/DomainResponse"},"type":"array","title":"Domains","description":"Domains connected to this account"}},"type":"object","required":["domains"],"title":"DomainListResponse","description":"List of domains for the account."},"DomainResponse":{"properties":{"object":{"type":"string","title":"Object","description":"Object type discriminator","default":"domain","examples":["domain"]},"id":{"type":"string","title":"Id","description":"Stable domain identifier","examples":["dom_01hzz00000000000000000"]},"hostname":{"type":"string","title":"Hostname","description":"Canonical hostname registered with dropthis","examples":["drops.nokia.com"]},"mode":{"type":"string","title":"Mode","description":"Mount mode: 'path' (many drops at hostname/{slug}/) or 'dedicated' (one drop at hostname/)","examples":["path"]},"status":{"type":"string","title":"Status","description":"Lifecycle status: pending_dns | verifying | live | failed","examples":["pending_dns"]},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason","description":"Reason for failure status","examples":["cloudflare_validation_failed"]},"default":{"type":"boolean","title":"Default","description":"Whether this is the account's default publish domain","examples":[false]},"drop_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drop Id","description":"Mounted drop id (dedicated mode only)","examples":["drop_01hzz00000000000000000"]},"dns":{"items":{"$ref":"#/components/schemas/DnsRecord"},"type":"array","title":"Dns","description":"DNS records required for this domain"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp","examples":["2026-06-11T12:00:00Z"]},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At","description":"When the domain first reached 'live' status","examples":["2026-06-11T12:05:00Z"]},"next":{"items":{"$ref":"#/components/schemas/NextHint"},"type":"array","title":"Next","description":"Structured next-step hints for the agent"},"console_url":{"type":"string","title":"Console Url","description":"Deep-link to manage this domain in the web console (finish DNS setup, check status)","examples":["https://app.dropthis.app/domains/dom_01hzz00000000000000000"]}},"type":"object","required":["id","hostname","mode","status","default","dns","created_at","next","console_url"],"title":"DomainResponse","description":"Full domain resource representation."},"DropAction":{"properties":{"code":{"type":"string","title":"Code","description":"Stable machine-readable action code","examples":["expires_on_free_tier"]},"kind":{"type":"string","title":"Kind","description":"Who can resolve: 'api' (agent) or 'human'","examples":["human"]},"priority":{"type":"string","title":"Priority","description":"'required' (blocks usage) or 'suggested' (caveat)","examples":["suggested"]},"message":{"type":"string","title":"Message","description":"Plain English explanation","examples":["This drop expires in 7 days."]},"resolve":{"anyOf":[{"$ref":"#/components/schemas/ActionResolve"},{"type":"null"}],"description":"How to resolve this action"}},"type":"object","required":["code","kind","priority","message"],"title":"DropAction"},"DropAnalyticsResponse":{"properties":{"views":{"type":"integer","title":"Views","description":"Approximate total canonical-view count","examples":[128]},"truncated":{"type":"boolean","title":"Truncated","description":"True when the event set exceeded the read cap and the figures are a lower bound","default":false},"by_country":{"anyOf":[{"items":{"$ref":"#/components/schemas/AnalyticsBreakdownItem"},"type":"array"},{"type":"null"}],"title":"By Country","description":"Top countries (full analytics only; null on view_count plans)"},"by_referer":{"anyOf":[{"items":{"$ref":"#/components/schemas/AnalyticsBreakdownItem"},"type":"array"},{"type":"null"}],"title":"By Referer","description":"Top referer hosts (full analytics only; null on view_count plans)"},"daily":{"anyOf":[{"items":{"$ref":"#/components/schemas/AnalyticsDailyPoint"},"type":"array"},{"type":"null"}],"title":"Daily","description":"Daily view series (full analytics only; null on view_count plans)"}},"type":"object","required":["views"],"title":"DropAnalyticsResponse","description":"Per-drop view analytics (F5). Counts are **best-effort** — captured at the\nedge fire-and-forget, so reloads/crawlers can over-count and a dropped edge\nwrite can under-count; treat as an approximate signal, not exact human visits.\n`view_count` plans get `views` only; `full` plans also get the breakdowns."},"DropDeploymentResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stable deployment identifier","examples":["dep_01hzz00000000000000000"]},"drop_id":{"type":"string","title":"Drop Id","description":"Parent drop identifier","examples":["drop_01hzz00000000000000000"]},"revision":{"type":"integer","title":"Revision","description":"Content revision for this drop","examples":[2]},"status":{"type":"string","title":"Status","description":"Deployment lifecycle status","examples":["published"]},"entry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry","description":"Entry path served at the drop root","examples":["index.html"]},"content_type":{"type":"string","title":"Content Type","description":"Entry MIME type","examples":["text/html"]},"render_mode":{"type":"string","title":"Render Mode","description":"Viewer render mode","examples":["user_html"]},"files":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Files","description":"Published file manifest","examples":[[{"content_type":"text/html","path":"index.html","size_bytes":1024}]]},"warnings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Warnings","description":"Bundle classification warnings","examples":[[{"code":"large_asset","message":"Large asset detected"}]]},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Total deployment size in bytes","examples":[12345]},"classification_version":{"type":"integer","title":"Classification Version","description":"Bundle classifier version","examples":[1]},"classification_reason":{"type":"string","title":"Classification Reason","description":"Bundle classifier reason code","examples":["html_bundle"]},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code","description":"Failure code when deployment failed","examples":["upload_failed"]},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Failure message when deployment failed","examples":["Storage upload failed"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp","examples":["2026-05-23T12:00:00Z"]},"ready_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ready At","description":"Files uploaded timestamp","examples":["2026-05-23T12:00:05Z"]},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Public route metadata switch timestamp","examples":["2026-05-23T12:00:06Z"]}},"type":"object","required":["id","drop_id","revision","status","content_type","render_mode","size_bytes","classification_version","classification_reason","created_at"],"title":"DropDeploymentResponse"},"DropResponse":{"properties":{"object":{"type":"string","title":"Object","description":"Object type discriminator","default":"drop","examples":["drop"]},"id":{"type":"string","title":"Id","description":"Stable drop identifier","examples":["drop_01hzz00000000000000000"]},"slug":{"type":"string","title":"Slug","description":"Public slug used in the drop URL","examples":["summer-launch"]},"url":{"type":"string","title":"Url","description":"Permanent public URL for the drop","examples":["https://dropthis.com/p/summer-launch"]},"raw_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Raw Url","description":"Public URL of the raw bytes for a single non-HTML file drop (render_mode 'file_viewer'): the canonical url plus the entry filename at its natural path. Hand this to agents that want the bytes (the canonical url is the branded human view). Null for 'user_html' (the page is the artifact) and 'collection_viewer' (per-file natural paths come from the deployment manifest).","examples":["https://summer-launch.dropthis.app/report.md"]},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain","description":"Hostname of the custom domain this drop is mounted on; null for shared-pool drops","examples":["drops.example.com"]},"deployment_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deployment Id","description":"Currently live deployment id","examples":["dep_01hzz00000000000000000"]},"title":{"type":"string","title":"Title","description":"Human-readable drop title","examples":["Summer Launch"]},"content_type":{"type":"string","title":"Content Type","description":"Entry file MIME type","examples":["text/html"]},"visibility":{"type":"string","title":"Visibility","description":"Serving visibility","examples":["public"]},"status":{"type":"string","title":"Status","description":"Publishing lifecycle status","examples":["published"]},"revision":{"type":"integer","title":"Revision","description":"Monotonic content/settings revision","examples":[1]},"content_revision":{"type":"integer","title":"Content Revision","description":"Monotonic content deployment revision","examples":[1]},"access_revision":{"type":"integer","title":"Access Revision","description":"Monotonic access-control revision","examples":[1]},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Total published bundle size in bytes","examples":[12345]},"render_mode":{"type":"string","title":"Render Mode","description":"Viewer render mode","examples":["user_html"]},"warnings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Warnings","description":"Bundle classification warnings","examples":[[{"code":"external_script","message":"External script detected"}]]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp","examples":["2026-05-23T12:00:00Z"]},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last content or settings change timestamp","examples":["2026-05-24T09:30:00Z"]},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Human-facing label of the app/key that created the drop, joined from the creating credential; null when attribution is unknown (e.g. console-session publishes or revoked keys).","examples":["Claude Desktop"]},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Optional automatic expiry timestamp","examples":["2026-06-23T12:00:00Z"]},"noindex":{"type":"boolean","title":"Noindex","description":"Whether search-engine indexing is disabled","examples":[false]},"password_protected":{"type":"boolean","title":"Password Protected","description":"Whether a view password is set (the password itself is never returned)","examples":[false]},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"Custom user or agent metadata stored with the drop","examples":[{"campaign":"fall-launch","prompt_id":"msg_456"}]},"accessible":{"type":"boolean","title":"Accessible","description":"Whether the drop is currently accessible","examples":[true]},"persistent":{"type":"boolean","title":"Persistent","description":"Whether this drop persists indefinitely","examples":[false]},"badge_applied":{"type":"boolean","title":"Badge Applied","description":"Whether a dropthis badge was injected","examples":[false]},"tier":{"$ref":"#/components/schemas/TierInfo","description":"Tier limits and characteristics for this drop"},"limitations":{"$ref":"#/components/schemas/Limitations","description":"Caveats and suggested actions for this drop"},"workspace":{"$ref":"#/components/schemas/DropWorkspace","description":"Workspace that owns this drop"}},"type":"object","required":["id","slug","url","title","content_type","visibility","status","revision","content_revision","access_revision","size_bytes","render_mode","created_at","updated_at","noindex","password_protected","accessible","persistent","badge_applied","tier","limitations","workspace"],"title":"DropResponse"},"DropWorkspace":{"properties":{"id":{"type":"string","title":"Id","description":"Workspace identifier","examples":["ws_01hzz00000000000000000"]},"name":{"type":"string","title":"Name","description":"Workspace display name","examples":["My Team"]},"slug":{"type":"string","title":"Slug","description":"Workspace slug","examples":["my-team"]},"kind":{"type":"string","title":"Kind","description":"Workspace kind: 'personal' or 'team'","examples":["personal"]}},"type":"object","required":["id","name","slug","kind"],"title":"DropWorkspace"},"EmailOtpRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email address that should receive the OTP","examples":["ada@example.com"]}},"type":"object","required":["email"],"title":"EmailOtpRequest"},"EmailVerifyRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email address that received the OTP","examples":["ada@example.com"]},"code":{"type":"string","maxLength":6,"minLength":6,"pattern":"^\\d{6}$","title":"Code","description":"Six-digit one-time passcode from the email","examples":["123456"]}},"type":"object","required":["email","code"],"title":"EmailVerifyRequest"},"EntitlementLimits":{"properties":{"max_size_bytes":{"type":"integer","title":"Max Size Bytes","description":"Maximum size of a single drop in bytes","examples":[5242880]},"max_storage_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Storage Bytes","description":"Total account storage cap in bytes; null means no account-level cap","examples":[524288000]},"default_ttl_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Default Ttl Seconds","description":"Drop lifetime in seconds before expiry; null means drops are permanent","examples":[2592000]},"max_custom_hostnames":{"type":"integer","title":"Max Custom Hostnames","description":"Maximum number of custom hostnames the workspace may connect; 0 means custom domains require Pro","examples":[0]},"seat_limit":{"type":"integer","title":"Seat Limit","description":"Maximum members the workspace may hold (owner included)","examples":[1]},"max_active_upload_sessions":{"type":"integer","title":"Max Active Upload Sessions","description":"Maximum concurrent in-flight upload sessions (a transient concurrency cap)","examples":[20]}},"type":"object","required":["max_size_bytes","max_storage_bytes","default_ttl_seconds","max_custom_hostnames","seat_limit","max_active_upload_sessions"],"title":"EntitlementLimits"},"Entitlements":{"properties":{"capabilities":{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"string"}]},"type":"object","title":"Capabilities","description":"Per-capability state for the active plan. Boolean caps are true/false; enum caps (og_preview, analytics) carry a value (compare by value, never truthiness).","examples":[{"analytics":"view_count","custom_domain":false,"password_protect":false}]},"required_plan":{"additionalProperties":{"type":"string"},"type":"object","title":"Required Plan","description":"The lowest plan that unlocks each gated capability — drives the upgrade nudge. Enum sub-values are keyed as `og_preview.custom_image` / `analytics.full`.","examples":[{"analytics.full":"pro","custom_expiry":"keep","password_protect":"pro"}]},"limits":{"$ref":"#/components/schemas/EntitlementLimits","description":"Numeric limits for the active plan"}},"type":"object","required":["capabilities","required_plan","limits"],"title":"Entitlements","description":"The full capability matrix for the caller's active plan — the single read\na client/agent uses to pre-check gates before attempting an operation."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HandoffRedeemRequest":{"properties":{"code":{"type":"string","minLength":1,"title":"Code","description":"The single-use handoff code from the URL fragment"}},"type":"object","required":["code"],"title":"HandoffRedeemRequest"},"HandoffResponse":{"properties":{"object":{"type":"string","const":"handoff","title":"Object","default":"handoff"},"code":{"type":"string","title":"Code","description":"Single-use auto-login code. Open the console at <route>#code=<code>.","examples":["xZ9...long-url-safe-token"]},"state":{"type":"string","title":"State","description":"CSRF binding value. The caller plants this in a partitioned cookie on the console's partition (never the URL); redeem requires the cookie to match.","examples":["aB3...long-url-safe-token"]},"expires_in":{"type":"integer","title":"Expires In","description":"Seconds until the handoff code expires","examples":[60]}},"type":"object","required":["code","state","expires_in"],"title":"HandoffResponse"},"IngestSessionResponse":{"properties":{"staged":{"type":"integer","title":"Staged","description":"Number of remote-fetch files now staged in the session (cumulative across all ingest attempts, not just this run).","examples":[1]},"status":{"type":"string","title":"Status","description":"Upload session lifecycle state after ingest (typically 'created', ready for completion).","examples":["created"]}},"type":"object","required":["staged","status"],"title":"IngestSessionResponse"},"InvitationListResponse":{"properties":{"invitations":{"items":{"$ref":"#/components/schemas/InvitationResponse"},"type":"array","title":"Invitations","description":"The matching invitations"}},"type":"object","required":["invitations"],"title":"InvitationListResponse"},"InvitationResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stable invitation identifier","examples":["inv_01hzz0000000000000000000"]},"workspace_id":{"type":"string","title":"Workspace Id","description":"The workspace the invitee is being invited to"},"email":{"type":"string","title":"Email","description":"Normalized invitee email","examples":["teammate@example.com"]},"role":{"type":"string","title":"Role","description":"Role to grant on accept","examples":["admin","member"]},"status":{"type":"string","title":"Status","description":"Invitation status","examples":["pending","accepted","revoked"]},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"When the invitation link expires"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the invitation was created"},"accepted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Accepted At","description":"When the invitation was accepted, if any"}},"type":"object","required":["id","workspace_id","email","role","status","expires_at","created_at"],"title":"InvitationResponse"},"InviteRequest":{"properties":{"email":{"type":"string","title":"Email","description":"Invitee email (normalized server-side)","examples":["teammate@example.com"]},"role":{"$ref":"#/components/schemas/WorkspaceRole","description":"Role to grant on accept (admin or member)","examples":["member","admin"]}},"additionalProperties":false,"type":"object","required":["email","role"],"title":"InviteRequest"},"KeyType":{"type":"string","enum":["delegated","service"],"title":"KeyType","description":"How a credential resolves its target workspace (the cross-surface workspace model).\n\n``delegated`` is the *default* a human/agent gets by logging in (CLI ``dropthis login``,\nMCP OAuth grant, extension BFF, console session): account-scoped, carrying an\n``allowed_workspace_ids`` allowlist and a **server-side, switchable** active workspace, and\naccepting a per-call workspace override. ``service`` is the explicit CI/automation opt-in:\nworkspace-**pinned**, no switch, selector rejected — a leaked CI key only touches its one\nworkspace. Delegated keys are minted automatically (one per connected client / login) and\nshare a far more generous cap; service keys share the small, human-scale per-account cap."},"Limitations":{"properties":{"actions":{"items":{"$ref":"#/components/schemas/DropAction"},"type":"array","title":"Actions","description":"Caveats and suggested actions for this drop"}},"type":"object","title":"Limitations"},"ListDropDeploymentsResponse":{"properties":{"deployments":{"items":{"$ref":"#/components/schemas/DropDeploymentResponse"},"type":"array","title":"Deployments","description":"Deployments for the drop"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor for the next page","examples":["eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0yM1QxMjowMDowMFoiLCJpZCI6ImRlcF8ifQ=="]}},"type":"object","required":["deployments"],"title":"ListDropDeploymentsResponse"},"ListDropsResponse":{"properties":{"drops":{"items":{"$ref":"#/components/schemas/DropResponse"},"type":"array","title":"Drops","description":"Drops owned by the authenticated account"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Cursor for the next drop of results","examples":["eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0yM1QxMjowMDowMFoiLCJpZCI6ImRyb3BfIn0="]}},"type":"object","required":["drops"],"title":"ListDropsResponse"},"MemberListResponse":{"properties":{"members":{"items":{"$ref":"#/components/schemas/MemberResponse"},"type":"array","title":"Members","description":"Every member of the workspace"}},"type":"object","required":["members"],"title":"MemberListResponse"},"MemberResponse":{"properties":{"account_id":{"type":"string","title":"Account Id","description":"The member's account identifier","examples":["acc_01hzz0000000000000000000"]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"The member's email, or null if the account was deleted","examples":["member@example.com"]},"role":{"type":"string","title":"Role","description":"The member's role in the workspace","examples":["owner","admin","member"]},"is_you":{"type":"boolean","title":"Is You","description":"Whether this member is the calling account"},"joined_at":{"type":"string","format":"date-time","title":"Joined At","description":"When this member joined the workspace (Membership.created_at)"}},"type":"object","required":["account_id","email","role","is_you","joined_at"],"title":"MemberResponse"},"NextHint":{"properties":{"action":{"type":"string","title":"Action","description":"Machine-readable action code","examples":["verify","dns","publish"]},"message":{"type":"string","title":"Message","description":"Plain English explanation of the action","examples":["Create CNAME drops.nokia.com → edge.dropthis.app at your DNS provider."]}},"type":"object","required":["action","message"],"title":"NextHint","description":"A next-step hint for the agent."},"OtpSentResponse":{"properties":{"ok":{"type":"boolean","const":true,"title":"Ok","default":true},"expires_in":{"type":"integer","title":"Expires In","description":"Seconds until the OTP expires","examples":[600]},"next_action":{"$ref":"#/components/schemas/Action","description":"Next step to complete authentication","default":{"code":"verify_email_otp","kind":"api","method":"POST","endpoint":"/v1/auth/email/verify","message":"Verify the 6-digit code sent to your email."}}},"type":"object","required":["expires_in"],"title":"OtpSentResponse"},"RefreshRequest":{"properties":{"refresh_token":{"type":"string","minLength":1,"title":"Refresh Token","description":"The rotating refresh token issued at session start or the previous refresh","examples":["rt_01hzz00000000000000000"]}},"type":"object","required":["refresh_token"],"title":"RefreshRequest"},"RenameWorkspaceRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"New workspace name (omit to leave unchanged)","examples":["Acme Inc"]},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug","description":"New URL-safe slug (omit to leave unchanged); must be unique","examples":["acme-inc"]}},"additionalProperties":false,"type":"object","title":"RenameWorkspaceRequest"},"ResolveRequest":{"properties":{"target":{"type":"string","maxLength":2048,"title":"Target","description":"A drop locator: a drop id (drop_…), a bare slug, or a full public URL (shared-pool or custom-domain). Resolved owner-scoped to its drop. Persist the drop_… id — URLs and slugs are locators, not identifiers.","examples":["https://summer-launch.dropthis.app/"]}},"additionalProperties":false,"type":"object","required":["target"],"title":"ResolveRequest"},"ResolveResponse":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/DropResponse"},{"type":"null"}],"description":"The resolved drop, or null if the target resolves to no drop the caller owns."}},"type":"object","title":"ResolveResponse"},"RevokeImpact":{"type":"string","enum":["disconnects_app","breaks_automation"],"title":"RevokeImpact","description":"Stable descriptor of *what breaks* when a key is revoked, so the console can render\n\"what happens if you revoke this\" copy without hard-coding it against the key type."},"SessionInfo":{"properties":{"object":{"type":"string","const":"session_info","title":"Object","default":"session_info"},"id":{"type":"string","title":"Id","description":"Session identifier","examples":["ses_01hzz00000000000000000"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the session was started","examples":["2026-06-16T12:00:00Z"]},"last_used_at":{"type":"string","format":"date-time","title":"Last Used At","description":"When the session was last refreshed","examples":["2026-06-16T12:30:00Z"]},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent","description":"User-Agent captured at session start","examples":["Mozilla/5.0"]},"current":{"type":"boolean","title":"Current","description":"Whether this is the session making the request","examples":[true]}},"type":"object","required":["id","created_at","last_used_at","current"],"title":"SessionInfo"},"SessionListResponse":{"properties":{"object":{"type":"string","const":"list","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/SessionInfo"},"type":"array","title":"Data","description":"Active sessions for the authenticated account"}},"type":"object","required":["data"],"title":"SessionListResponse"},"SessionResponse":{"properties":{"object":{"type":"string","const":"session","title":"Object","default":"session"},"token":{"type":"string","title":"Token","description":"Bearer session token","examples":["sess_01hzz00000000000000000"]},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Id","description":"Authenticated account identifier (omitted on refresh, where the token alone authenticates)","examples":["acc_01hzz00000000000000000"]},"is_new_account":{"type":"boolean","title":"Is New Account","description":"Whether verification created a new account","default":false,"examples":[false]},"expires_in":{"type":"integer","title":"Expires In","description":"Seconds until the session token expires","examples":[2592000]},"refresh_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token","description":"Rotating refresh token for the console browser session. Present when a session is started (email/verify, refresh); null for non-session token issuance.","examples":["rt_01hzz00000000000000000"]}},"type":"object","required":["token","expires_in"],"title":"SessionResponse"},"SetActiveWorkspaceRequest":{"properties":{"workspace":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace","description":"Workspace slug or id to act within on subsequent requests; the caller must be a member. Pass null to reset to the personal workspace.","examples":["ws_01hzz00000000000000000","acme"]}},"additionalProperties":false,"type":"object","required":["workspace"],"title":"SetActiveWorkspaceRequest"},"StepUpRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email address of the authenticated account that received the step-up OTP","examples":["ada@example.com"]},"code":{"type":"string","maxLength":6,"minLength":6,"pattern":"^\\d{6}$","title":"Code","description":"Six-digit one-time passcode from the email","examples":["123456"]}},"type":"object","required":["email","code"],"title":"StepUpRequest"},"TierInfo":{"properties":{"name":{"type":"string","title":"Name","description":"Plan tier name","examples":["free"]},"max_size_bytes":{"type":"integer","title":"Max Size Bytes","description":"Maximum drop size in bytes","examples":[5242880]},"ttl_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ttl Days","description":"Default TTL in days, null if persistent","examples":[7]},"persistent":{"type":"boolean","title":"Persistent","description":"Whether drops persist indefinitely on this tier","examples":[false]},"badge":{"type":"boolean","title":"Badge","description":"Whether this tier injects a dropthis badge","examples":[true]}},"type":"object","required":["name","max_size_bytes","ttl_days","persistent","badge"],"title":"TierInfo"},"TransformRequest":{"properties":{"width":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Width","description":"Max output width in pixels (fit inside, no upscale)."},"height":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Height","description":"Max output height in pixels (fit inside, no upscale)."},"quality":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Quality","description":"Encoder quality 1-100 (jpeg/webp)."},"format":{"anyOf":[{"type":"string","enum":["jpeg","png","webp"]},{"type":"null"}],"title":"Format","description":"Output format. Omit to keep the source raster format.","examples":["jpeg"]}},"type":"object","title":"TransformRequest","description":"Optional image transform applied server-side on a `source_url` fetch (#82):\nthe server resizes/re-encodes the fetched bytes before staging, so an agent can\npoint at a big original and store a small web-optimised derivative. Fit-inside\nthe given box, aspect preserved, never upscaled; metadata stripped."},"UnlockRequest":{"properties":{"drop_id":{"type":"string","maxLength":200,"minLength":1,"title":"Drop Id"},"password":{"type":"string","maxLength":1024,"minLength":1,"title":"Password"}},"type":"object","required":["drop_id","password"],"title":"UnlockRequest"},"UnlockResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"access_revision":{"type":"integer","title":"Access Revision"}},"type":"object","required":["ok","access_revision"],"title":"UnlockResponse"},"UpdateAccountRequest":{"properties":{"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"New display name, or null to clear it","examples":["Ada"]}},"additionalProperties":false,"type":"object","title":"UpdateAccountRequest"},"UpdateDomainRequest":{"properties":{"drop_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drop Id","description":"Repoint dedicated domain to this drop id","examples":["drop_01hzz00000000000000000"]},"default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Default","description":"Set or clear the account publish default (path domains only)","examples":[true]}},"additionalProperties":false,"type":"object","title":"UpdateDomainRequest","description":"PATCH /v1/domains body. ``extra=\"forbid\"``: unknown keys are rejected at\nthe schema boundary with 422 — they must never reach the service's keyword\nsignature (a key like ``account`` would collide with its parameters)."},"UploadSessionFileResponse":{"properties":{"file_id":{"type":"string","title":"File Id","description":"Server-assigned file identifier for this manifest entry.","examples":["file_000001"]},"path":{"type":"string","title":"Path","description":"Normalized path for this file inside the staged artifact.","examples":["index.html"]},"content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Type","description":"Declared MIME type that completion checks against stored object metadata. Null for remote-fetch files until ingested.","examples":["text/html"]},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes","description":"Declared byte size that completion checks against the stored object size. Null for remote-fetch files until ingested.","examples":[204800]},"object_key":{"type":"string","title":"Object Key","description":"Internal staging object key for the uploaded bytes; this is not a public URL.","examples":["staging/acc_01hzz00000000000000000/upl_01hzz00000000000000000/files/000001"]},"verified":{"type":"boolean","title":"Verified","description":"Whether completion has verified that the staged object matches the manifest entry.","examples":[false]},"origin":{"type":"string","title":"Origin","description":"How this file enters staging: 'client_put' (client uploads bytes to the signed target) or 'remote_fetch' (server fetches from source_url).","examples":["client_put"]},"state":{"type":"string","title":"State","description":"Ingest lifecycle state for remote-fetch files: 'pending', 'fetching', 'staged', or 'failed'. Always 'pending' for client_put files until ingested.","examples":["pending"]},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Public http(s) URL the server fetches into staging. Null for client_put files.","examples":["https://cdn.example.com/assets/logo.png"]}},"type":"object","required":["file_id","path","object_key","verified","origin","state"],"title":"UploadSessionFileResponse"},"UploadSessionResponse":{"properties":{"upload_id":{"type":"string","title":"Upload Id","description":"Stable upload session identifier to pass to completion and later publish calls.","examples":["upl_01hzz00000000000000000"]},"status":{"type":"string","title":"Status","description":"Current upload session lifecycle state.","examples":["created"]},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Time when the upload session stops accepting normal completion attempts.","examples":["2026-05-15T12:00:00Z"]},"entry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry","description":"Path of the entry file that will be served first after the staged artifact is published.","examples":["index.html"]},"files":{"items":{"$ref":"#/components/schemas/UploadSessionFileResponse"},"type":"array","title":"Files","description":"Manifest entries tracked by this upload session, including verification state.","examples":[[{"content_type":"text/html","file_id":"file_000001","object_key":"staging/acc_01hzz00000000000000000/upl_01hzz00000000000000000/files/000001","path":"index.html","size_bytes":204800,"verified":false}]]},"next_action":{"anyOf":[{"$ref":"#/components/schemas/Action"},{"type":"null"}],"description":"Next step based on current upload session status","readOnly":true}},"type":"object","required":["upload_id","status","expires_at","files","next_action"],"title":"UploadSessionResponse"},"UploadTargetResponse":{"properties":{"file_id":{"type":"string","title":"File Id","description":"Server-assigned file identifier for this manifest entry.","examples":["file_000001"]},"path":{"type":"string","title":"Path","description":"Normalized path for this file inside the staged artifact.","examples":["index.html"]},"object_key":{"type":"string","title":"Object Key","description":"Internal staging object key for the uploaded bytes; this is not a public URL.","examples":["staging/acc_01hzz00000000000000000/upl_01hzz00000000000000000/files/000001"]},"upload":{"additionalProperties":true,"type":"object","title":"Upload","description":"Direct storage upload instructions. Send bytes to the returned URL with exactly the provided headers.","examples":[{"expires_at":"2026-05-15T12:00:00Z","headers":{"content-type":"text/html"},"strategy":"single_put","url":"https://storage.example/upload/index.html"}]}},"type":"object","required":["file_id","path","object_key","upload"],"title":"UploadTargetResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WorkspaceListResponse":{"properties":{"workspaces":{"items":{"$ref":"#/components/schemas/WorkspaceResponse"},"type":"array","title":"Workspaces","description":"Every workspace the caller is a member of, including the personal workspace"}},"type":"object","required":["workspaces"],"title":"WorkspaceListResponse"},"WorkspaceResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stable workspace identifier","examples":["ws_01hzz00000000000000000"]},"name":{"type":"string","title":"Name","description":"Workspace name","examples":["Acme Corp"]},"slug":{"type":"string","title":"Slug","description":"URL-safe workspace slug","examples":["acme-corp"]},"kind":{"type":"string","title":"Kind","description":"Workspace kind","examples":["team","personal"]},"plan":{"type":"string","title":"Plan","description":"Active billing plan tier of the workspace","examples":["free","pro"]},"role":{"type":"string","title":"Role","description":"The calling account's role in this workspace","examples":["owner","admin","member"]},"is_active":{"type":"boolean","title":"Is Active","description":"Whether this is the caller's currently-active workspace"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Workspace creation timestamp","examples":["2026-06-18T12:00:00Z"]},"creator_can_reach":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Creator Can Reach","description":"On CREATE only (null on read responses): whether the credential that created this workspace can act in it. False means the creating key was allowlist-restricted and the new workspace is NOT in its allowlist (ADR 0068 §6) — the key will 403 on the next write to it; re-grant a credential that reaches this workspace (e.g. log in again)."}},"type":"object","required":["id","name","slug","kind","plan","role","is_active","created_at"],"title":"WorkspaceResponse"},"WorkspaceRole":{"type":"string","enum":["owner","admin","member"],"title":"WorkspaceRole"},"Problem":{"type":"object","title":"Problem","description":"RFC 7807 problem+json error document. Every 4xx/5xx response uses this shape. Branch on `code`, gate retries on `retryable`, surface `suggestion` to the user, and quote `request_id` in support reports. Conditional fields appear only on the errors that produce them (e.g. `current_revision` on a revision conflict, `required_scope` on insufficient_scope, the byte fields on a size quota).","properties":{"type":{"type":"string","format":"uri","description":"A URI reference identifying the problem type (https://dropthis.app/errors/{code}).","examples":["https://dropthis.app/errors/quota_exceeded"]},"title":{"type":"string","description":"Short, human-readable summary of the problem type."},"status":{"type":"integer","description":"The HTTP status code.","examples":[403]},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence."},"code":{"type":"string","description":"Machine-readable error code — the field SDKs/agents branch on. OPEN vocabulary (80+ values across the auth, upload, source_url, slug, workspace, and plan families, and growing): a client must accept any string and treat an unknown code as the generic class for the HTTP status. Common values are listed in the API description.","examples":["quota_exceeded","insufficient_scope","validation_error","not_found"]},"retryable":{"type":"boolean","description":"Whether retrying the identical request can succeed. An agent retry layer must stop when false."},"suggestion":{"type":"string","description":"An actionable next step the CLI/SDK/MCP render verbatim to the user."},"request_id":{"type":"string","description":"Correlation id for this request; quote it in support reports."},"message":{"type":"string","description":"Raw error message; equals `detail` on typed errors."},"param":{"type":["string","null"],"description":"The offending request field, or null when the error is not field-specific."},"current_revision":{"type":"integer","description":"The drop's current revision (revision_conflict) — retry with this as If-Revision."},"required_scope":{"type":"string","description":"The capability scope the credential is missing (insufficient_scope)."},"feature":{"type":"string","description":"The capability that is gated (feature_not_in_plan)."},"current_plan":{"type":"string","description":"The caller's current plan (plan/quota errors)."},"required_plan":{"type":"string","description":"The lowest plan that unlocks the feature."},"upgrade_url":{"type":"string","format":"uri","description":"Where a human can upgrade the plan."},"limit":{"type":"integer","description":"The numeric ceiling that was hit (quota_exceeded)."},"used":{"type":"integer","description":"The amount already consumed (quota_exceeded)."},"requested":{"type":"integer","description":"The amount requested (quota_exceeded)."},"size_bytes":{"type":"integer","description":"This drop's size in bytes (per-drop size quota)."},"max_size_bytes":{"type":"integer","description":"The per-drop byte cap for the current plan."},"storage_used_bytes":{"type":"integer","description":"Workspace storage already used (storage quota)."},"max_storage_bytes":{"type":"integer","description":"The workspace storage cap for the current plan."},"failures":{"type":"array","description":"Per-file remote-fetch failures (remote_fetch_failed): {path, source_url, reason}.","items":{"type":"object"}}},"required":["type","title","status","detail","code","retryable"]}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer","bearerFormat":"API Key (sk_) / access token (at_)","description":"Send `Authorization: Bearer <token>`. Use an `sk_` API key (mint via `POST /v1/api-keys` or `dropthis login`) or an `at_` console access token. Capability is carried by the credential's scopes (ADR 0068); a default login is publish-only."}}},"tags":[{"name":"Auth","description":"Email OTP login, verification, step-up, and session lifecycle."},{"name":"API Keys","description":"Mint and manage `sk_` API keys (downscope-only, capability-scoped)."},{"name":"Uploads","description":"Staged upload sessions: open a session, then complete it before publishing a drop."},{"name":"Drops","description":"Publish, read, update content/settings, list, resolve, and delete drops and their deployments."},{"name":"Account","description":"The authenticated account: profile, entitlements, active workspace, and deletion."},{"name":"Workspaces","description":"Workspace CRUD and member management (capability-scoped control plane)."},{"name":"Invitations","description":"Invite teammates to a workspace and accept invitations."},{"name":"Domains","description":"Connect, verify, and manage custom domains."},{"name":"Edge","description":"Edge-serving helpers (e.g. password unlock) consumed by the CF viewer."}]}