Filings
Filings prepares your tax returns from your calculated transactions, lets you review and approve each one, and files it for you. It is available for any country or scheme the filing service supports; US states are one example. The API covers the whole cycle: set filing up for a registration, follow and approve each return, and add your own transaction data to a month when part of your sales is not calculated through Clearvo.
https://api.clearvo.io/v1) and the same csk_live_* / csk_test_* keys as the rest of the API. The key needs the filings product (or all). Reads accept a read key; every action that changes something needs a write key (a read key gets 403). Account-scoped keys also send x-entity-id.The flow
- Set up filing for each registration that can file: choose how often you file and when to start.
- Clearvo prepares each return from your transactions. Review it and approve it, or let it approve automatically at the end of the review window.
- If some of your sales are not calculated through Clearvo, upload them for the month before the return is approved.
- Clearvo submits and files the approved return. You are notified by webhook at each step.
Set up filing
GET /v1/filings/setups returns one row per registered registration with where setup stands and what to do next. Registrations that cannot file (a local registration, or one that cannot be filed yet) are listed with eligible: false and the reason; while filing data is temporarily unavailable the reason says so. Rows that need action come first.
curl https://api.clearvo.io/v1/filings/setups \
-H "x-api-key: csk_live_..."
| Field | Description |
|---|---|
| state | NOT_SET_UP, REQUESTED (waiting for Clearvo to finish onboarding), ENABLED or STOPPED_AFTER. |
| nextStep | SET_UP, ADD_PAYMENT_ACCOUNT, WAIT_FOR_ONBOARDING, RESTART or NONE. nextStepLabel, stateLabel and actionLabel are ready-to-show sentences. |
| filingAccess | Returned once, not per row. canSetUp: false means requesting filing is refused with 402 plan_required until the account subscribes; message says why. |
GET /v1/tax/registrations/{id}/filing returns the full setup for one registration: the filing frequencies the tax authority offers (frequencyOptions), the start periods on offer for each (startOptionsByFrequency), whether a bank account is required and canStart / startBlockedReason. Display text such as frequencyQuestion and frequencyAttestationLabel is written to be shown as is.
PUT /v1/tax/registrations/{id}/filing takes exactly one of three bodies:
curl https://api.clearvo.io/v1/tax/registrations/{registrationId}/filing \
-X PUT \
-H "x-api-key: csk_live_..." \
-H "Content-Type: application/json" \
-d '{ "frequencyCode": "Monthly", "frequencyAttested": true, "startChoice": "asap" }'
| Body | Effect |
|---|---|
frequencyCode, frequencyAttested: true, startChoice (asap or later; later also needs startPeriodKey from startOptionsByFrequency[frequencyCode].later) | Request filing. frequencyAttested confirms the frequency is the one on your tax account. Filing starts once Clearvo has completed onboarding. When the tax is paid by debit, a bank account must be on file first (422 BANK_ACCOUNT_REQUIRED); bank details are entered in the dashboard only. |
deferFirstPeriodTo (date) | Move the first filing period later, before it has been prepared. |
stop: true | Stop filing from the next period. |
Setup errors are 422 with error, field and detail: START_CHOICE_REQUIRED, START_PERIOD_REQUIRED, INVALID_START_PERIOD, FREQUENCY_NOT_OFFERED, FREQUENCY_ATTESTATION_REQUIRED, BANK_ACCOUNT_REQUIRED. If filing is not available for the registration yet the call is refused with FILING_NOT_AVAILABLE_LOCALITY or FILING_NOT_AVAILABLE_JURISDICTION. Requesting filing without a subscription is 402 plan_required; deferring and stopping are never refused for that reason. When filing data is temporarily unavailable the call is 503 FILING_DATA_UNAVAILABLE: try again shortly; it says nothing about whether the registration is supported. frequencyOptionsSource in the setup is live, or fallback while the tax authority source is unreachable (the three standard frequencies with estimated start periods). GET /v1/filings/bank-account shows the account the tax authority debits (last four digits only).
Returns lifecycle
GET /v1/filings lists your returns (filter by view=open|upcoming|filed|backdated, backdated=true|false, status, jurisdictionId; page with limit and offset). view=open is every return not yet filed, backdated ones included, soonest due date first; view=filed is every filed return. A return is backdated when it covers an earlier period you paid us to catch up on; backdated is combined with view, so view=backdated&backdated=false is always empty. When a list is empty, emptyState.title is the wording to show. GET /v1/filings/{id} returns one return with its totals, lines by tax authority, scope notes, approval, payment and a timeline. allowedActions says which actions are possible now.
curl "https://api.clearvo.io/v1/filings?view=upcoming" \
-H "x-api-key: csk_live_..."
Statuses
| Status | Meaning |
|---|---|
DRAFT | Being prepared. |
PENDING_APPROVAL | Ready for your review. Approves automatically at the end of its last review day unless paused or blocked; nextStepLabel states when. |
PAUSED | You paused it so it is not approved automatically. Pausing is limited per year, and on-time filing is no longer guaranteed while a return is paused. |
APPROVED | Approved for submission. You can still undo until it is submitted. |
SUBMITTED | Sent to the tax authority. |
FILED | Filed; the proof of filing is available when attached (GET /v1/filings/{id}/proof). |
NOT_FILED | Skipped (with your reason) or closed without filing. |
Actions
Each action is a POST with the return's current snapshotVersion (from the list or detail). If the return changed since you looked you get 409 FILING_STALE with currentSnapshotVersion: re-read and decide again. Repeating an action that already holds returns 200 with alreadyApplied: true.
curl https://api.clearvo.io/v1/filings/{filingId}/approve \
-X POST \
-H "x-api-key: csk_live_..." \
-H "Content-Type: application/json" \
-d '{ "snapshotVersion": 3 }'
| Path | Description |
|---|---|
POST /v1/filings/{id}/approve | Approve for submission. 409 CONTENT_MISSING or 409 BANK_DETAILS_REQUIRED when it cannot proceed. |
POST /v1/filings/{id}/undo | Take an approval back to PENDING_APPROVAL before submission (409 ALREADY_SUBMITTED afterwards). |
POST /v1/filings/{id}/pause, /resume | Pause or resume. Refusals: PAUSE_NOT_ALLOWED, PAUSE_LIMIT_REACHED, PAUSE_PAST_CUTOFF, INVALID_TRANSITION. |
POST /v1/filings/{id}/skip | Body snapshotVersion and reason (up to 500 characters). The return becomes NOT_FILED and is not filed. |
POST /v1/filings/{id}/prepayment | Record an amount already paid for the period (snapshotVersion, amount). Only before approval. |
GET|POST /v1/filings/{id}/change-requests | List or send a request to change something that cannot be edited directly (the transactions in a return are frozen once it is prepared). |
GET /v1/filings/{id}/csv, /transactions.csv | Download the summary by authority or the transaction lines of the return. |
Nothing in the API can approve a return except your own approve call or the automatic approval at the end of the review window. Submitting, filing and attaching proof are done by Clearvo.
Returns read live
A return whose id starts with ret_ is read live from the filing service on every call (live: true). It has its own statuses, a documents list on the detail, and three actions: approve, pause and resume. It has no snapshotVersion; the return itself decides whether it can still be approved or paused.
| Status | Meaning |
|---|---|
IN_PREPARATION | Being prepared. |
READY_FOR_APPROVAL | Ready for your review. actionNeeded says by when to approve. |
APPROVED | Approved. We file it before the due date. |
FILED | Filed with the tax authority. |
NEEDS_ATTENTION | Our team is looking into it. |
jurisdictionId is a country code such as GB or DE, a state such as US_CA, or an EU scheme: EU (EU sales), EN (sales by sellers outside the EU) or XM (imports). Show jurisdictionLabel, statusLabel, amountLabel and the other labels as they are.
Approve. POST /v1/filings/{id}/approve needs a write key and no body. Offer it only when allowedActions.approve is true on the detail, and show approveConfirmation first. An approval cannot be undone, withdrawn or changed: undo, skip, prepayment, change requests, the CSV downloads and the proof download answer 404 FILING_NOT_FOUND for a ret_ id. Success is 200 with message and the refreshed filing; approving a return that is already approved is the same 200 with alreadyApplied: true.
| Status | error | When |
|---|---|---|
| 402 | plan_required | The account has no filing subscription. |
| 403 | FORBIDDEN | The key is read-only. |
| 409 | NOT_AWAITING_APPROVAL | The return is not ready for approval. |
| 409 | BANK_DETAILS_REQUIRED | The bank account the tax authority will debit is missing. Add it in the dashboard. |
| 409 | APPROVAL_NOT_COMPLETED | The approval was recorded but the return is not yet approved; our team has been told. |
| 503 | AUDIT_UNAVAILABLE | The approval could not be recorded, so nothing was approved. Try again. |
| 503 | APPROVAL_UNCONFIRMED | The outcome could not be confirmed. Read the return before trying again. |
| 503 | FILING_DATA_UNAVAILABLE | Filing data is temporarily unavailable (any live read). Try again shortly. |
Pause and resume. While a return waits for approval, POST /v1/filings/{id}/pause holds back our finalising of it so the customer has more time to review. Offer it only when pause.canPause is true on the list or detail; when it is false, pause.canPauseReason says why in plain words. The body is optional: endDate (yyyy-mm-dd) is the last day of the pause. It must fall within pause.allowedEnd (earliest to latest, up to 10 days, never after the 15th of the month after the period and never on or after the last day the return can be approved); omit it for the latest allowed day. POST /v1/filings/{id}/resume ends an open pause early and needs no body; offer it only when pause.canResume is true. After a pause ends or is resumed we wait until the day after its last day before we finalise, so the customer keeps a fair window. A return can be paused once, an organisation can pause at most 5 returns in any 12 months, and a backdated return, a return we will not finalise automatically and an approved return cannot be paused. The customer can approve at any time, paused or not. Both calls answer 200 with message and the refreshed filing; a repeat is the same 200 with alreadyApplied: true. Every attempt is recorded in the audit log.
| Status | error | When |
|---|---|---|
| 402 | plan_required | The account has no filing subscription. |
| 403 | FORBIDDEN | The key is read-only. |
| 409 | NOT_AWAITING_APPROVAL | The return is not waiting for approval. |
| 409 | PAUSE_NOT_ALLOWED | The return is backdated, will not be finalised automatically, or was already paused once. |
| 409 | PAUSE_PAST_CUTOFF | It is too late to pause this return. |
| 409 | PAUSE_LIMIT_REACHED | The organisation has used all 5 pauses for the year. |
| 409 | NOT_PAUSED | Resume only: the return is not paused, or its pause already ran out. |
| 422 | INVALID_PAUSE_END | endDate is not a date or is outside allowedEnd; the response carries allowedEnd. |
| 503 | AUDIT_UNAVAILABLE | The change could not be recorded, so nothing changed. Try again. |
Every return also carries reviewLabel, one plain sentence on what happens next while it waits for approval (for example the review date, or the end of a pause). Show it as it is. A paused return keeps the status READY_FOR_APPROVAL; pause.isPaused and pause.pausedUntilLabel say that it is paused.
Documents. The detail lists each document with id, name, label and downloadPath. GET /v1/filings/{id}/documents/{docId} streams the file as an attachment; nothing is stored.
Try it in the sandbox
With a csk_test_* key, POST /v1/filings/sandbox/generate with { "registrationId": "..." } (and optionally a periodKey) builds a return for a registration whose filing frequency is set, so you can try review, approve, pause and skip without waiting for the daily job. A live key gets 409 SANDBOX_ONLY.
Upload your data
Transactions you calculate through Clearvo are added to each month's return automatically (the month carries a file that is marked source: "system"). If part of your sales happens elsewhere, add that data as files for the month. You can add one file or several files for the same month: every file in the month is processed together, and you confirm the month once when everything is in.
1. Find the month
GET /v1/filings/upload/months lists the months you can add data to, newest first. linked is false until your business profile is set up for filing.
curl https://api.clearvo.io/v1/filings/upload/months \
-H "x-api-key: csk_live_..."
| state | Meaning |
|---|---|
not_open | The period has not finished yet. A month opens on the day after its last day. |
open | Takes data. |
processing | Your data is being processed. Changes are possible once it finishes. |
done | Processed without errors. |
errors | Processed, but a file has errors or a file failed. The month cannot be confirmed until the file is fixed or removed. |
closed | Read-only, kept for older months. The month's message says to contact support. |
locked | The return for the month is approved or submitted. If it is only approved, undo the approval to change data; once submitted the month is final. |
The window. A month takes data from the moment its period closes until the end of the day on which its return is approved automatically, which is the 5th of the following month by default. window.opensAt and window.closesAt give the exact instants for each month. There is no call to close a month: a month is finished by approving its return, and an earlier-period month stays open until a submitted or approved return locks it.
2. Get the template
GET /v1/filings/upload/template returns the columns Clearvo expects, in csv (default), xlsx (with a field guide sheet) or json (columns, formats, examples and guidance). Dates are yyyy-MM-dd and amounts use a . decimal point with no thousands separator.
# CSV (default), or ?format=xlsx, or ?format=json
curl "https://api.clearvo.io/v1/filings/upload/template?format=csv" \
-H "x-api-key: csk_live_..." -o filing-data-template.csv
3. Add files
POST /v1/filings/upload/months/{month}/files takes multipart form data with the file in the field file (.csv, .xlsx or .xls, up to 25 MB) and needs a write key. Send the request with a Content-Length header (411 LENGTH_REQUIRED otherwise). Call it again for each further file in the month.
curl https://api.clearvo.io/v1/filings/upload/months/2026-09/files \
-X POST \
-H "x-api-key: csk_live_..." \
-F "file=@september-sales.csv"
{
"ok": true,
"file": {
"id": "file_01j9...",
"name": "september-sales.csv",
"source": "customer",
"status": "uploaded",
"statusLabel": "Not processed yet",
"transactionCount": null,
"errorCount": null,
"errorReportAvailable": false
}
}
A file with the same name or the same content as one already in the month is refused with 409 FILE_ALREADY_ADDED (the body carries existingId, the id of the file already in the month, and reason: same_name or same_content). To replace a file, delete it with that id and upload the new one. Refusals specific to files: 413 FILE_TOO_LARGE, 415 UNSUPPORTED_FILE_TYPE.
DELETE /v1/filings/upload/months/{month}/files/{fileId} removes a file you added while the month is open. The file Clearvo calculates for you cannot be removed (409 SYSTEM_FILE_NOT_DELETABLE). After you add or delete a file, the month must be processed again.
4. Check the month
GET /v1/filings/upload/months/{month} returns the files with their counts, the totals, the window, canUpload / canConfirm, allowedActions (upload, delete, process) and the declaration you accept in the next step. While any file has errors, canConfirm and allowedActions.process are false and message says what to do (with contactUrl when the file is one prepared for you).
curl https://api.clearvo.io/v1/filings/upload/months/2026-09 \
-H "x-api-key: csk_live_..."
5. Process the month once
POST /v1/filings/upload/months/{month}/process confirms that all data for the month is added and processes it once. Needs a write key. The body is only declarationAccepted and declarationVersion; any other field is refused with 400 invalid_body. The person confirming accepts a declaration: show them month.declaration.text and send back declarationAccepted: true with the declarationVersion from the same response (currently 2026-10-13.1). The acceptance is recorded with who, when, the version and the files and their counts.
curl https://api.clearvo.io/v1/filings/upload/months/2026-09/process \
-X POST \
-H "x-api-key: csk_live_..." \
-H "Content-Type: application/json" \
-d '{ "declarationAccepted": true, "declarationVersion": "2026-10-13.1" }'
- No acceptance, or
false:422 DECLARATION_REQUIRED. A version that is no longer current:422 DECLARATION_VERSION_MISMATCHwithcurrentVersionand the currentdeclaration(version,text); show the new text and resend with its version. - It returns
202while processing runs. PollGET /v1/filings/upload/months/{month}untilstateisdoneorerrors. - An error in any row fails the whole file: the file is not used until you fix it and upload it again. While any file in the month has errors the call is refused with
409 FILE_ERRORS(the body carriesmessageandfilesWithErrors), which blocks the whole month. Look at the error reports, fix the file and upload it again, or delete it, then confirm. Warnings never block. - If the file with errors is one prepared for you, the call is
409 SYSTEM_FILE_ERRORSinstead (message,filesWithErrorsandcontactUrl): you cannot change that file, so contact support. 409 PROCESSING_REFUSEDmeans the month cannot be processed yet; itsmessagesays why.409 MONTH_NOT_OPENandMONTH_CLOSEDmean the month is not open for data, or its upload window has closed.409 SYSTEM_FILE_PENDINGmeans your calculated transactions are still being added to the month: try again in a few minutes.- Repeating the call when nothing has changed since the last clean run returns
200withalreadyApplied: true.
File statuses and the error report
| status | Meaning |
|---|---|
uploaded | Not processed yet. Counts are null. |
processed | Read without errors. transactionCount is the number of transactions found. A processed file with warnings still has an error and warning report. |
had_errors | A row has an error, so the whole file is not used: errorCount says how many rows and errorReportAvailable is true. |
failed | The file could not be processed. |
GET /v1/filings/upload/months/{month}/files/{fileId}/error-report returns a fresh download link (url, https only) to the error and warning report of a processed file, with the reason for each row. It is available for a processed file with errors or warnings. The link is new on every call and is for you only, so do not store it. A file with neither returns 404 no_error_report. Fix the rows in your source, delete the file, upload the corrected one and process the month again.
curl https://api.clearvo.io/v1/filings/upload/months/2026-09/files/{fileId}/error-report \
-H "x-api-key: csk_live_..."
{ "ok": true, "name": "september-sales-errors.csv", "url": "https://...", "label": "Download error and warning report" }
Upload errors
| Status | error | When |
|---|---|---|
| 402 | plan_required | Filing needs a subscription. |
| 409 | MONTH_NOT_OPEN, MONTH_CLOSED, MONTH_LOCKED (carries reason), PERIOD_PROCESSING (carries state), PERIOD_CONFLICT | The month cannot take changes now: not open yet, window closed, locked, or being processed. |
| 409 | FILE_ALREADY_ADDED (carries existingId, reason), SYSTEM_FILE_NOT_DELETABLE | Duplicate file, or deleting the calculated file. |
| 409 | FILE_ERRORS (carries filesWithErrors), SYSTEM_FILE_ERRORS (carries filesWithErrors, contactUrl) | A file has errors, which blocks the month: fix the file and upload it again or delete it, then confirm (for a file prepared for you, contact support). |
| 409 | SYSTEM_FILE_PENDING, PROCESSING_REFUSED | Processing is not possible yet: wait for the calculated transactions, or see message. |
| 409 | month_not_available, filing_backend_not_linked | The month is not available, or your business profile is not set up for data upload yet. Contact support. |
| 422 | DECLARATION_REQUIRED, DECLARATION_VERSION_MISMATCH (carries currentVersion, declaration) | The declaration was not accepted or is out of date. |
| 503 | processing_backend_unavailable | Temporarily unavailable. Try again later. |
| 503 | FILING_DATA_UNAVAILABLE | Setup only: filing data is temporarily unavailable. Try again shortly. |
All upload errors are { "error": "CODE", "message": "..." } and the message is plain language you can show to a person. Extra fields (existingId, reason, filesWithErrors, contactUrl, currentVersion, declaration) are top-level fields of the body; in the SDK they are on ClearvoError.body, and in MCP tool errors they follow Details:.
Webhooks
Register for filing events with POST /v1/webhooks (or subscribe to *). Each payload carries event, eventId, filingId, jurisdictionId, periodKey, filingStatus, dueDate and amountDue, and is signed like every other webhook. country is the ISO country of the return (US for US_CA); for a filing under an OSS jurisdiction it is null and jurisdictionId identifies the return. Sandbox filings fire to sandbox endpoints only.
Events: filing.enabled, filing.ready, filing.approved, filing.paused, filing.resumed, filing.pause_ending, filing.payment_due, filing.submitted, filing.filed, filing.skipped, filing.not_filed, filing.action_needed.
See Webhooks for signing and retries, and the endpoint reference for every request and response.