Support
A signed-in person's own support requests, the conversation with Swaps Support and the files they send in it. Dashboard session only.
Jump to an operation:
- GET /support_cases
- POST /support_cases
- POST /support_cases/attachments
- GET /support_cases/attachments/policy
- GET /support_cases/attachments/{id}
- DELETE /support_cases/attachments/{id}
- POST /support_cases/attachments/{id}/complete
- GET /support_cases/related_objects
- GET /support_cases/summary
- GET /support_cases/{id}
- GET /support_cases/{id}/messages
- POST /support_cases/{id}/messages
List the person's support requests — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
The signed-in person's own support requests, newest created first, optionally one status_group. Dashboard session only, never a business key: a request belongs to the person who wrote it, not to an account. Swaps-Account only picks the membership that authenticates the call; the list does not change with it. Each request says whether it takes files (accepts_attachments) and how many more (attachments_left). Test mode: unavailable. For the waiting count read GET /v1/support_cases/summary instead of counting pages.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
status_groupOne status group. The set is closed: any other value is 400 invalid_request naming this parameter.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List the person's support requests — dark-flag › Responses
A page of the person's support requests.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Open or join a support request — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Opens a support request about one of the signed-in person's topics and, optionally, one of their own objects: an order, a payment link, an invoice payment or a pay run (related_object, an id from GET /v1/support_cases/related_objects). When the person already has an open request they opened themselves with the same question, the text joins it instead and the answer is 200 with that request rather than 201: the same topic and object, the Identity verification topic, or the Business verification topic for the same account. A request about no object on any other topic always opens a new one. A Business verification request is about the account the call authenticates (Swaps-Account, else your default account). Another person's object, one that does not exist and a malformed id all answer 404; a type the topic does not take is 400. The text is 3 to 10,000 characters with one that is not a space, and no control character but tab and line feed. Never include passwords, API keys, recovery phrases, documents or bank details in it. attachments names up to 5 files the person already uploaded without a support_case and had checked (ready; see POST /v1/support_cases/attachments); they go on the new request, or on the reply when the text joins an open one. A request with files still needs words. Screenshots, receipts and PDFs help. Never send passwords, keys or recovery phrases. The Identity verification and Business verification topics take no files (409 attachments_not_accepted), and a file that is not ready, belongs to a request, is already on a message or was removed is 409 attachment_unavailable (param: attachments[i]). A person may open 10 requests in 10 minutes and 50 a day, and a text that joins counts as a reply (10 in 10 minutes, 60 a day): past that the answer is 429 with Retry-After, nothing was stored, and after waiting you send again under a new Idempotency-Key. While objects are switched off the answer is 503 with details.reason: "feature_disabled", and so is a non-empty attachments while files are switched off; without files the operation is unchanged. Dashboard session only; an owner or admin session (a member session is 403). Test mode: unavailable.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Open or join a support request — dark-flag › Request Body
The intake registry branch the request is filed under, as SupportCase.topic: the topic the person picked, or the topic of the related object's row.
bodyPlain text, 3 to 10,000 characters (Unicode code points) after trimming, at least one that is not a space, and no control character but tab and line feed; the request as stored is at most 32 KiB. Never include passwords, API keys, recovery phrases, documents or bank details.
related_objectThe prefixed id of one of the person's own orders (ord_), payment links (pl_), invoice payments (po_) or pay runs (pr_), from GET /v1/support_cases/related_objects; omitted or null for a request about no object. Another person's object, one that does not exist and a malformed id are 404, never 400.
localeThe language the person wrote in, one the dashboard offers; default en.
entrypointWhere the person wrote from; default dashboard. The set is closed.
attachmentsUp to 5 files the person uploaded without a support_case and had checked (ready), by their ids from POST /v1/support_cases/attachments, in the order they should show. They go on the new request, or on the reply when the text joins an open one. A request with files still needs words. Screenshots, receipts and PDFs help. Never send passwords, keys or recovery phrases. The Identity verification and Business verification topics take no files (409 attachments_not_accepted).
Open or join a support request — dark-flag › Responses
The person's open request with the same question, which the text joined as their newest message.
id^sc_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhat the person sees. waiting_on_customer is "Waiting for your answer": the case is waiting and the newest visible message is from Swaps Support. with_support is "With our team", every other open request. closed is "Closed". An open enum: treat a value you do not recognise as an opaque string.
status_groupThe bucket of status, and the filter GET /v1/support_cases takes.
topicThe intake registry keys the request was filed under, shown through the support_intake.products.<key> and .intents.<key> strings. null for a request from before intake v2.
subjectThe request's subject: what the person typed in the older form, or the registry label pair of a newer request. Show the topic label when topic is set.
The newest visible message, or the opening request when nothing followed.
closed_atSet while status is closed: the resolution time, else the request's last update (a request closed before the case workflow).
reply_emailWhere Swaps Support's replies are also emailed: the person's own address as stored with the request; null when none is deliverable.
The object the request is about: the one Swaps Support linked it to, else the one the person chose when they wrote. Checked again against the person on every read: null once the object is no longer theirs, and for a request about no object, a verification request, a request written without signing in and every request from before objects existed.
accepts_attachmentsWhether this request takes files now. false while files are switched off, on the Identity verification and Business verification topics and on a verification partner's request (sending one is 409 attachments_not_accepted).
attachments_leftHow many more files this request can take: 25 less the files on it and the uploads of it still in flight. 0 whenever accepts_attachments is false.
updated_atStart sending a file — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Reserves a slot for one file the signed-in person wants to send to Swaps Support and returns where to put its bytes: upload is a one-time PUT target (url, the headers to send with it and expires_at; a slot lasts 15 minutes). The bytes never pass through /v1. PUT the raw file to upload.url with those headers, then call POST /v1/support_cases/attachments/{id}/complete so Swaps checks it, and only then name the id in a message (attachments of POST /v1/support_cases/{id}/messages, or of POST /v1/support_cases for a new request). support_case is the request the file is for when it goes with a reply; leave it out for a file that goes with a new request. PNG, JPEG, WebP, HEIC, HEIF and PDF, up to 10 MiB (10,485,760 bytes) a file, 5 a message and 25 a request: GET /v1/support_cases/attachments/policy lists them. The type is declared here and Swaps decides by the file's bytes. A request that cannot take files (the Identity verification and Business verification topics, a verification partner's question) is 409 attachments_not_accepted; past 25 on the request or 10 open uploads it is 409 attachment_limit_reached (details.limit). A person may start 20 uploads in 10 minutes and 60 a day, and declare 200 MiB a day: past that the answer is 429 with Retry-After, nothing was stored, and after waiting you send again under a new Idempotency-Key. A replay of the same key answers the same file with upload: null and filename: null, because the token is never stored and neither is the person's file name (read it with GET /v1/support_cases/attachments/{id}): delete that file and start again under a new key. Another person's request and one that does not exist answer 404. While files are switched off the answer is 503 with details.reason: "feature_disabled". Dashboard session only. Test mode: unavailable.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Start sending a file — dark-flag › Request Body
filenameThe file's name as the person has it. Swaps cleans it (control and direction characters removed, anything up to the last / or \ dropped, spaces collapsed, at most 200 characters keeping the extension); a name that is empty after cleaning is 400 attachment_filename_invalid. It is shown, escaped, to the person and to Swaps Support, and is never put in a URL, a storage key or a log line.
content_typeThe type the client says the file has. A closed set: any other value is 400 attachment_type_not_allowed. Swaps decides the stored type from the bytes, so this never sets it.
sizeThe size in bytes the client says the file has, 1 to 10,485,760 (10 MiB); a larger one is 400 attachment_too_large. The check compares it with the bytes that arrive (size_mismatch).
support_case^sc_The request the file is for, when it goes with a reply. Omit it for a file that goes with a new request. Another person's request and one that does not exist are 404.
Start sending a file — dark-flag › Responses
The file's slot, awaiting_upload, with upload saying where to put the bytes (upload and filename are null on a replay of the same Idempotency-Key).
id^sca_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhere the file is. awaiting_upload: the slot is open and no bytes have been checked. checking: Swaps is checking them. ready: checked and stored, not yet on a message. attached: on a message. rejected: the check refused the bytes (see rejection). removed: deleted by the person, by time or by Swaps Support. An open enum: treat a value you do not recognise as an opaque string.
filenameThe person's own name for the file, cleaned (no control or direction characters, no path, at most 200 characters): show it escaped. null once the file is rejected or removed, and in the answer to a replay of the same Idempotency-Key of create or complete, which keeps no name: read it with GET /v1/support_cases/attachments/{id}. It is never in a URL, a storage key or a log line.
content_typeThe type Swaps found in the bytes, never the one declared; null until the check has run.
kindimage or pdf, from content_type; null until the check has run.
sizeThe size in bytes Swaps stored; null until the check has run.
widthPixels, for an image once checked; null for a PDF and before the check.
heightPixels, for an image once checked; null for a PDF and before the check.
message^scm_ · requiredThe message the file is on once it is attached; null before.
Why the check refused the bytes; null unless status is rejected.
download_urlA link that downloads the file (it carries Content-Disposition: attachment), valid until download_url_expires_at. null unless status is ready or attached and files are switched on, and when signing the link failed. A bearer link: do not store or share it.
download_url_expires_atWhen download_url stops working (10 minutes after it was made); null with it.
updated_atsupport_case^sc_The request the file is for; null for a file that goes with a new request until that request is opened. Absent inside a message's attachments, which the message already names.
Where to put the bytes. Present only on the answer of POST /v1/support_cases/attachments, and null there when the answer is a replay of the same Idempotency-Key, because the token is never stored. Absent everywhere else.
What files a request can take — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
The rules for files on support requests: the largest file in bytes, the most files on one message and on one request, the six types, and the topics that take no files (Identity verification and Business verification: «ID documents go through Verification.»). The same for every person; whether one request takes files now is SupportCase.accepts_attachments. While files are switched off the answer is 503 with details.reason: "feature_disabled", which is how a client knows not to offer them. Dashboard session only. Test mode: unavailable.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
What files a request can take — dark-flag › Responses
The limits, the types and the topics without files.
max_file_bytesThe largest file, in bytes (10 MiB).
max_files_per_messageThe most files on one message.
max_files_per_caseThe most files on one request, all its messages together.
content_typesThe types a file may be. An open list, so treat a value you do not recognise as an opaque string.
The topics (as SupportCase.topic names them) whose requests take no files: every branch of Identity verification and Business verification, whatever the intent. The field there says: «ID documents go through Verification.»
Retrieve a file — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
One of the person's own files: its status and, while it is ready or attached and files are switched on, a fresh download_url that expires at download_url_expires_at (10 minutes). Use it to read the outcome of a complete whose answer was lost, and to renew a link. The link always downloads (it carries Content-Disposition: attachment) and is a bearer: do not store or share it. Another person's file, an unknown id and a malformed id answer 404. Dashboard session only. Test mode: unavailable.
path Parameters
id^sca_ · requiredHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Retrieve a file — dark-flag › Responses
The file.
id^sca_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhere the file is. awaiting_upload: the slot is open and no bytes have been checked. checking: Swaps is checking them. ready: checked and stored, not yet on a message. attached: on a message. rejected: the check refused the bytes (see rejection). removed: deleted by the person, by time or by Swaps Support. An open enum: treat a value you do not recognise as an opaque string.
filenameThe person's own name for the file, cleaned (no control or direction characters, no path, at most 200 characters): show it escaped. null once the file is rejected or removed, and in the answer to a replay of the same Idempotency-Key of create or complete, which keeps no name: read it with GET /v1/support_cases/attachments/{id}. It is never in a URL, a storage key or a log line.
content_typeThe type Swaps found in the bytes, never the one declared; null until the check has run.
kindimage or pdf, from content_type; null until the check has run.
sizeThe size in bytes Swaps stored; null until the check has run.
widthPixels, for an image once checked; null for a PDF and before the check.
heightPixels, for an image once checked; null for a PDF and before the check.
message^scm_ · requiredThe message the file is on once it is attached; null before.
Why the check refused the bytes; null unless status is rejected.
download_urlA link that downloads the file (it carries Content-Disposition: attachment), valid until download_url_expires_at. null unless status is ready or attached and files are switched on, and when signing the link failed. A bearer link: do not store or share it.
download_url_expires_atWhen download_url stops working (10 minutes after it was made); null with it.
updated_atsupport_case^sc_The request the file is for; null for a file that goes with a new request until that request is opened. Absent inside a message's attachments, which the message already names.
Where to put the bytes. Present only on the answer of POST /v1/support_cases/attachments, and null there when the answer is a replay of the same Idempotency-Key, because the token is never stored. Absent everywhere else.
Remove a file — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Removes a file the person has not sent yet: one that is awaiting_upload or ready. The answer is the file with status: removed, and its bytes are deleted. A file that is already on a message cannot be removed here (409 attachment_unavailable). Another person's file, an unknown id and a malformed id answer 404. Dashboard session only. Test mode: unavailable.
path Parameters
id^sca_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Remove a file — dark-flag › Responses
The file, removed.
id^sca_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhere the file is. awaiting_upload: the slot is open and no bytes have been checked. checking: Swaps is checking them. ready: checked and stored, not yet on a message. attached: on a message. rejected: the check refused the bytes (see rejection). removed: deleted by the person, by time or by Swaps Support. An open enum: treat a value you do not recognise as an opaque string.
filenameThe person's own name for the file, cleaned (no control or direction characters, no path, at most 200 characters): show it escaped. null once the file is rejected or removed, and in the answer to a replay of the same Idempotency-Key of create or complete, which keeps no name: read it with GET /v1/support_cases/attachments/{id}. It is never in a URL, a storage key or a log line.
content_typeThe type Swaps found in the bytes, never the one declared; null until the check has run.
kindimage or pdf, from content_type; null until the check has run.
sizeThe size in bytes Swaps stored; null until the check has run.
widthPixels, for an image once checked; null for a PDF and before the check.
heightPixels, for an image once checked; null for a PDF and before the check.
message^scm_ · requiredThe message the file is on once it is attached; null before.
Why the check refused the bytes; null unless status is rejected.
download_urlA link that downloads the file (it carries Content-Disposition: attachment), valid until download_url_expires_at. null unless status is ready or attached and files are switched on, and when signing the link failed. A bearer link: do not store or share it.
download_url_expires_atWhen download_url stops working (10 minutes after it was made); null with it.
updated_atsupport_case^sc_The request the file is for; null for a file that goes with a new request until that request is opened. Absent inside a message's attachments, which the message already names.
Where to put the bytes. Present only on the answer of POST /v1/support_cases/attachments, and null there when the answer is a replay of the same Idempotency-Key, because the token is never stored. Absent everywhere else.
Finish an upload — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Call it after the PUT of the bytes. Swaps reads what was stored, decides the type from the bytes (a name or a declared type never decides), measures an image, removes the GPS location from a photo, refuses what it cannot accept and keeps its own checked copy. 200 with the file ready: its stored content_type, size and, for an image, width and height. 422 attachment_rejected when the bytes are refused, with details.reason one of type_not_allowed, type_mismatch, size_mismatch, empty, incomplete, image_dimensions, image_unreadable, pdf_unreadable, pdf_encrypted or pdf_active_content; nothing is kept and the file is rejected. 409 attachment_not_uploaded when the bytes have not landed yet (PUT, then call again), 409 attachment_check_in_progress with Retry-After: 5 while a check of the same file is running, and 409 attachment_expired when the slot or the check ran out: start over. A file that is ready and not sent in a message within 24 hours expires. If the answer is lost, read the file with GET /v1/support_cases/attachments/{id}; a replay of the same Idempotency-Key answers the same file with filename: null, because the person's file name is not stored with the answer. Another person's file, an unknown id and a malformed id answer 404. Dashboard session only. Test mode: unavailable.
path Parameters
id^sca_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Finish an upload — dark-flag › Request Body
Finish an upload — dark-flag › Responses
The file, checked and stored (ready).
id^sca_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhere the file is. awaiting_upload: the slot is open and no bytes have been checked. checking: Swaps is checking them. ready: checked and stored, not yet on a message. attached: on a message. rejected: the check refused the bytes (see rejection). removed: deleted by the person, by time or by Swaps Support. An open enum: treat a value you do not recognise as an opaque string.
filenameThe person's own name for the file, cleaned (no control or direction characters, no path, at most 200 characters): show it escaped. null once the file is rejected or removed, and in the answer to a replay of the same Idempotency-Key of create or complete, which keeps no name: read it with GET /v1/support_cases/attachments/{id}. It is never in a URL, a storage key or a log line.
content_typeThe type Swaps found in the bytes, never the one declared; null until the check has run.
kindimage or pdf, from content_type; null until the check has run.
sizeThe size in bytes Swaps stored; null until the check has run.
widthPixels, for an image once checked; null for a PDF and before the check.
heightPixels, for an image once checked; null for a PDF and before the check.
message^scm_ · requiredThe message the file is on once it is attached; null before.
Why the check refused the bytes; null unless status is rejected.
download_urlA link that downloads the file (it carries Content-Disposition: attachment), valid until download_url_expires_at. null unless status is ready or attached and files are switched on, and when signing the link failed. A bearer link: do not store or share it.
download_url_expires_atWhen download_url stops working (10 minutes after it was made); null with it.
updated_atsupport_case^sc_The request the file is for; null for a file that goes with a new request until that request is opened. Absent inside a message's attachments, which the message already names.
Where to put the bytes. Present only on the answer of POST /v1/support_cases/attachments, and null there when the answer is a replay of the same Idempotency-Key, because the token is never stored. Absent everywhere else.
List what the person can write about — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
What the signed-in person can write to Support about: their own orders, payment links, invoice payments and pay runs, and their verification, each with the open request on it when there is one. The two verification rows come first, then the rows with an open request, then the rest, newest first. Every row with an id is one POST /v1/support_cases accepts as related_object, under the row's topic; a verification row has no id (send its topic alone). id asks for one object's row: another person's object, one that does not exist and a malformed id are an empty list, never an error. While objects are switched off the answer is 503 with details.reason: "feature_disabled". Not a product list: read /v1/orders, /v1/payment_links, /v1/payouts or /v1/payroll_runs for the objects themselves. Dashboard session only. Test mode: unavailable.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
typeWhich rows, comma-separated: order, payment_link, payout, payroll_run, verification, business_verification (default all). The set is closed: any other value is 400 invalid_request naming this parameter. Not combined with id.
idThe prefixed id of one object (ord_, pl_, po_ or pr_): its row, or none. Not combined with type.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List what the person can write about — dark-flag › Responses
A page of rows, verification first, then rows with an open request, then newest first.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Count the person's support requests — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Unbounded counts of the person's support requests per status group, over every request, not just a loaded page. needs_you is the number waiting for the person's answer, the figure the dashboard shows beside Support. Dashboard session only. Test mode: unavailable.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Count the person's support requests — dark-flag › Responses
Unbounded counts per status group.
Unbounded counts over every support request the person owns, like ActivitySummary: a cursor list never carries a total.
Retrieve a support request — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
One of the person's own support requests, with whether it takes files (accepts_attachments) and how many more (attachments_left). Another person's request, an unknown id and a request that was not filed from a signed-in session all answer 404. Dashboard session only. Test mode: unavailable.
path Parameters
id^sc_ · requiredHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Retrieve a support request — dark-flag › Responses
The support request.
id^sc_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atstatusWhat the person sees. waiting_on_customer is "Waiting for your answer": the case is waiting and the newest visible message is from Swaps Support. with_support is "With our team", every other open request. closed is "Closed". An open enum: treat a value you do not recognise as an opaque string.
status_groupThe bucket of status, and the filter GET /v1/support_cases takes.
topicThe intake registry keys the request was filed under, shown through the support_intake.products.<key> and .intents.<key> strings. null for a request from before intake v2.
subjectThe request's subject: what the person typed in the older form, or the registry label pair of a newer request. Show the topic label when topic is set.
The newest visible message, or the opening request when nothing followed.
closed_atSet while status is closed: the resolution time, else the request's last update (a request closed before the case workflow).
reply_emailWhere Swaps Support's replies are also emailed: the person's own address as stored with the request; null when none is deliverable.
The object the request is about: the one Swaps Support linked it to, else the one the person chose when they wrote. Checked again against the person on every read: null once the object is no longer theirs, and for a request about no object, a verification request, a request written without signing in and every request from before objects existed.
accepts_attachmentsWhether this request takes files now. false while files are switched off, on the Identity verification and Business verification topics and on a verification partner's request (sending one is 409 attachments_not_accepted).
attachments_leftHow many more files this request can take: 25 less the files on it and the uploads of it still in flight. 0 whenever accepts_attachments is false.
updated_atList a support request's messages — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.read· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
The conversation, newest first: the opening request, the person's own replies and the messages Swaps Support sent. Notes, drafts, unsent messages and provider correspondence never appear. Every message carries attachments, the files the person sent with it ([] for none): the name, type and size of each and, while it can be opened, a download_url that expires after 10 minutes. While files are switched off they still list, with download_url: null. Dashboard session only. Test mode: unavailable.
path Parameters
id^sc_ · requiredquery Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
List a support request's messages — dark-flag › Responses
A page of visible messages, newest first.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Reply to a support request — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: dashboard session · Scope:
support.write· Test mode: unavailableThis operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
Adds the person's reply to their own support request. A reply to a closed request reopens it. The text is 1 to 10,000 characters (counted as Unicode code points) with at least one that is not a space, and no control character other than tab, line feed and carriage return; any field besides body and attachments is 400. Never include passwords, API keys, recovery phrases, documents or bank details in it. attachments names up to 5 files the person uploaded for this request and had checked (ready; see POST /v1/support_cases/attachments), in the order they should show. A reply with files still needs words. Screenshots, receipts and PDFs help. Never send passwords, keys or recovery phrases. A file that is not ready, belongs to another request, is already on a message or was removed is 409 attachment_unavailable (param: attachments[i]); past 5 on the message or 25 on the request, 409 attachment_limit_reached (details.limit); a request that takes no files (Identity verification, Business verification, a verification partner's question), 409 attachments_not_accepted. Nothing is stored unless the whole reply is. While files are switched off a non-empty attachments is 503 with details.reason: "feature_disabled"; without files the operation is unchanged. Swaps Support answers in the conversation and by email. A person may send 10 replies in 10 minutes and 60 in a day: past that the answer is 429 with Retry-After up to the end of the refusing window, and the reply was not stored, so after waiting send it again under a new Idempotency-Key (a 4xx completes its key). Dashboard session only. Test mode: unavailable.
path Parameters
id^sc_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
Reply to a support request — dark-flag › Request Body
bodyPlain text, 1 to 10,000 characters (Unicode code points), at least one that is not a space; no control characters but tab, line feed and carriage return. Never include passwords, API keys, recovery phrases, documents or bank details. With files the text is still required.
attachmentsUp to 5 files the person uploaded for this request and had checked (ready), by their ids from POST /v1/support_cases/attachments, in the order they should show. Screenshots, receipts and PDFs help. Never send passwords, keys or recovery phrases. Omit it, or send [], for a reply without files. A file that cannot go on this message is 409 attachment_unavailable with param: attachments[i].
Reply to a support request — dark-flag › Responses
The person's reply, as it now appears in the conversation.
id^scm_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectlivemodecreated_atsupport_case^sc_ · requiredThe support request the message belongs to.
authorWho wrote a message: customer is the signed-in person, swaps_support is Swaps Support. No operator is ever named. An open enum: treat a value you do not recognise as an opaque string.
bodyPlain text, line breaks kept. Never HTML or Markdown.
The files the person sent with this message, in the order they put them; [] for none, and always [] on a message from Swaps Support. The opening request (scm_ + the request's id) carries the files that went with the request. Inside a message a file has no support_case and no upload.
updated_at