API reference
Version 2026-09-06. Base URL https://api.taxpio.com. The machine-readable contract is at /openapi.json.
Platform
GET /v1
API index: version, documentation and where to start.
GET /v1/health
Liveness: the release and its database answer.
GET /v1/me
The workspace, application, environment and scopes behind the key.
Tax calculations
GET /v1/tax/programs
The calculation programs and their inputs.
POST /v1/tax/calculations
Run a deterministic tax calculation. Deterministic: the same request always returns the same lines. Send an Idempotency-Key to make retries safe; a replay carries `Idempotent-Replayed: true`.
| Parameter | In | Required | Type |
|---|---|---|---|
Idempotency-Key | header | no | string |
{
"type": "object",
"required": [
"jurisdiction",
"tax_year",
"inputs"
],
"properties": {
"jurisdiction": {
"type": "string",
"enum": [
"CA"
]
},
"program": {
"type": "string",
"enum": [
"T1",
"T2",
"1120",
"CT600",
"CIT"
],
"description": "Required for CA; defaults to the jurisdiction's program elsewhere."
},
"tax_year": {
"type": "integer",
"minimum": 2020,
"maximum": 2035
},
"inputs": {
"oneOf": [
{
"title": "CA/T1",
"type": "object",
"required": [
"facts"
],
"properties": {
"facts": {
"type": "object",
"description": "Personal facts: province, dateOfBirth, maritalStatus, eligibleDependant, …"
},
"slips": {
"type": "array",
"items": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"type": "string",
"enum": [
"T4",
"T4A",
"T4A_OAS",
"T4A_P",
"T4E",
"T5",
"T3",
"T5008",
"T2202",
"RRSP_RECEIPT",
"T2125",
"T776"
]
}
},
"additionalProperties": true
}
},
"claims": {
"type": "object",
"description": "rrspDeduction, professionalDues, childCareExpenses, otherEmploymentExpenses, medicalExpenses, charitableDonations, studentLoanInterest as decimal strings."
}
},
"description": "inputs = { facts, slips, claims } exactly as TaxPio's T1 rule package takes them: money as decimal strings in dollars, slips typed by kind (T4, T4A, T5, …)."
}
]
}
}
}GET /v1/tax/calculations
List calculations, newest first.
| Parameter | In | Required | Type |
|---|---|---|---|
limit | query | no | integer |
cursor | query | no | string · The previous page's next_cursor. |
GET /v1/tax/calculations/{calculationId}
Fetch one calculation.
| Parameter | In | Required | Type |
|---|---|---|---|
calculationId | path | yes | string |
Sandbox
GET /v1/sandbox/scenarios
Deterministic sandbox scenarios for every program and outcome. Sandbox keys only (tp_test_…). A live key receives 403 SANDBOX_ONLY.
POST /v1/sandbox/scenarios/{scenarioId}/run
Run a scenario as a real calculation in the sandbox.
| Parameter | In | Required | Type |
|---|---|---|---|
scenarioId | path | yes | string |
Idempotency-Key | header | no | string |
POST /v1/sandbox/webhook-sink/{endpointId}
A receiver on our host that verifies TaxPio's signature. Register a sandbox endpoint whose URL is https://api.taxpio.com/v1/sandbox/webhook-sink/{endpointId} (the id you get back) and every delivery lands here, is verified with your endpoint's secret, and shows in the trace.
| Parameter | In | Required | Type |
|---|---|---|---|
endpointId | path | yes | string |
Taxpio-Signature | header | yes | string |
Webhooks
GET /v1/webhooks
List webhook endpoints.
POST /v1/webhooks
Register an endpoint; the signing secret is returned once.
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "https only; a public host, or the sandbox sink on api.taxpio.com."
},
"description": {
"type": "string",
"maxLength": 500
},
"events": {
"type": "array",
"items": {
"type": "string",
"enum": [
"calculation.completed",
"key.revoked",
"webhook.test"
]
},
"description": "Defaults to every event."
}
}
}GET /v1/webhooks/{endpointId}
Fetch one endpoint.
| Parameter | In | Required | Type |
|---|---|---|---|
endpointId | path | yes | string |
DELETE /v1/webhooks/{endpointId}
Remove an endpoint and its deliveries.
| Parameter | In | Required | Type |
|---|---|---|---|
endpointId | path | yes | string |
POST /v1/webhooks/{endpointId}/test
Queue a webhook.test delivery.
| Parameter | In | Required | Type |
|---|---|---|---|
endpointId | path | yes | string |
GET /v1/webhooks/{endpointId}/deliveries
Recent deliveries to an endpoint.
| Parameter | In | Required | Type |
|---|---|---|---|
endpointId | path | yes | string |
limit | query | no | integer |
Connections
GET /v1/connections
Connections to client profiles.
| Parameter | In | Required | Type |
|---|---|---|---|
limit | query | no | integer |
cursor | query | no | string |
POST /v1/connections
Invite a client to connect a profile. TaxPio emails the client a link; they choose which of their profiles to connect and which permissions to grant. The connection is INVITED until then, and can be revoked by either side.
{
"type": "object",
"required": [
"client_email",
"system_name"
],
"properties": {
"client_email": {
"type": "string",
"format": "email"
},
"client_name": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"ACCOUNTING",
"ERP",
"ECOMMERCE",
"PAYROLL",
"PAYMENTS",
"CUSTOM"
]
},
"system_name": {
"type": "string",
"description": "The system the records come from, e.g. QuickBooks Online, Shopify, Gusto."
},
"permissions": {
"type": "array",
"items": {
"type": "string",
"enum": [
"records.import",
"records.read",
"documents.write"
]
},
"description": "Defaults to all three."
}
}
}GET /v1/connections/{connectionId}
Fetch one connection.
| Parameter | In | Required | Type |
|---|---|---|---|
connectionId | path | yes | string |
DELETE /v1/connections/{connectionId}
End a connection.
| Parameter | In | Required | Type |
|---|---|---|---|
connectionId | path | yes | string |
POST /v1/connections/{connectionId}/records
Push up to 500 records into the connected profile. Each record is identified by your external_id; a repeat of the same id is counted as a duplicate and never written twice. Amounts are signed decimal strings in the profile's currency: negative for money out, positive for money in. Records land as unreviewed transactions with receipt-match suggestions, exactly as a bank sync does.
| Parameter | In | Required | Type |
|---|---|---|---|
connectionId | path | yes | string |
Idempotency-Key | header | no | string |
{
"type": "object",
"required": [
"records"
],
"properties": {
"records": {
"type": "array",
"maxItems": 500,
"items": {
"$ref": "#/components/schemas/RecordInput"
}
}
}
}GET /v1/connections/{connectionId}/records
Records this connection pushed.
| Parameter | In | Required | Type |
|---|---|---|---|
connectionId | path | yes | string |
limit | query | no | integer |
cursor | query | no | string |
POST /v1/connections/{connectionId}/documents
Upload a document into the connected profile. multipart/form-data with the file (PDF, JPEG, PNG or WebP, up to 15 MB) plus optional metadata; the vault's pipeline and AI suggestions run as for any capture.
| Parameter | In | Required | Type |
|---|---|---|---|
connectionId | path | yes | string |
Idempotency-Key | header | no | string |
Usage and credits
GET /v1/usage
Requests per day and operation.
| Parameter | In | Required | Type |
|---|---|---|---|
from | query | no | string |
to | query | no | string |
GET /v1/credits
Credit balance and recent movements. Credits pay for billable filing actions on live keys and are consumed once per request (the request id is the idempotency key). A live call that needs more credits than the workspace holds answers 402 CREDITS_REQUIRED. Sandbox never bills.
Schemas
{
"type": "object",
"required": [
"error",
"code",
"requestId"
],
"properties": {
"error": {
"type": "string",
"description": "A sentence a person can read."
},
"code": {
"type": "string",
"enum": [
"BAD_REQUEST",
"UNAUTHENTICATED",
"PAYMENT_REQUIRED",
"FORBIDDEN",
"NOT_FOUND",
"METHOD_NOT_ALLOWED",
"NOT_ACCEPTABLE",
"REQUEST_TIMEOUT",
"CONFLICT",
"GONE",
"PAYLOAD_TOO_LARGE",
"UNSUPPORTED_MEDIA_TYPE",
"VALIDATION_FAILED",
"LOCKED",
"PRECONDITION_REQUIRED",
"RATE_LIMITED",
"INTERNAL",
"NOT_IMPLEMENTED",
"UNAVAILABLE",
"TIMEOUT",
"ORIGIN_REQUIRED",
"PASSKEY_REQUIRED",
"PASSKEY_ENROLLMENT_REQUIRED",
"RECENT_MFA_REQUIRED",
"RECENT_AUTHENTICATION_REQUIRED",
"ACCOUNT_PASSWORD_REQUIRED",
"EMAIL_VERIFICATION_REQUIRED",
"VERIFICATION_DELIVERY_FAILED",
"RESOURCE_ROUTE_UNAVAILABLE",
"MALWARE_DETECTED",
"NONDETERMINISTIC_PROVIDER_RETRY",
"CREDITS_REQUIRED",
"API_KEY_MISSING",
"API_KEY_INVALID",
"API_KEY_REVOKED",
"API_KEY_EXPIRED",
"WORKSPACE_SUSPENDED",
"APPLICATION_ARCHIVED",
"SCOPE_REQUIRED",
"PRODUCTION_REVIEW_REQUIRED",
"IDEMPOTENCY_KEY_INVALID",
"IDEMPOTENCY_KEY_REUSED",
"IDEMPOTENCY_KEY_IN_PROGRESS",
"BODY_INVALID",
"UNSUPPORTED_PROGRAM",
"SANDBOX_ONLY",
"CONNECTION_PENDING",
"CONNECTION_REVOKED",
"CONNECTION_PERMISSION_REQUIRED",
"LIVE_ONLY",
"WEBHOOK_SIGNATURE_INVALID"
],
"description": "What a client branches on."
},
"requestId": {
"type": "string",
"description": "Quote it to support; it is also the x-request-id response header."
},
"docs_url": {
"type": "string",
"format": "uri"
},
"problems": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}
}{
"type": "object",
"required": [
"id",
"object",
"environment",
"jurisdiction",
"program",
"tax_year",
"rule_package",
"currency",
"scope",
"reasons",
"notes",
"lines",
"inputs",
"input_hash",
"created_at"
],
"properties": {
"id": {
"type": "string",
"pattern": "^calc_[A-Za-z0-9]{20,32}$"
},
"object": {
"type": "string",
"enum": [
"tax.calculation"
]
},
"environment": {
"type": "string",
"enum": [
"live",
"sandbox"
]
},
"jurisdiction": {
"type": "string",
"enum": [
"CA"
]
},
"program": {
"type": "string",
"enum": [
"T1",
"T2"
]
},
"tax_year": {
"type": "integer"
},
"rule_package": {
"type": "string",
"description": "The versioned rule package that produced the answer."
},
"currency": {
"type": "string"
},
"scope": {
"type": "string",
"enum": [
"IN_SCOPE",
"OUT_OF_SCOPE",
"INCOMPLETE"
],
"description": "OUT_OF_SCOPE: the inputs describe a case the package refuses, with reasons. INCOMPLETE: CA/T1 with blocking issues."
},
"reasons": {
"type": "array",
"items": {
"type": "string"
}
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
},
"lines": {
"type": "array",
"items": {
"type": "object",
"required": [
"key",
"label"
],
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"amount_minor": {
"type": "integer",
"description": "Minor units (cents, pence, kobo) for preview programs."
},
"amount": {
"type": "string",
"description": "Decimal string in the currency for CA/T1."
},
"text": {
"type": "string"
}
}
}
},
"summary": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"severity": {
"type": "string"
},
"message": {
"type": "string"
},
"fix_action": {
"type": "string"
}
}
}
},
"inputs": {
"type": "object"
},
"input_hash": {
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
}
}
}{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"webhook_endpoint"
]
},
"environment": {
"type": "string",
"enum": [
"live",
"sandbox"
]
},
"url": {
"type": "string"
},
"description": {
"type": "string",
"nullable": true
},
"events": {
"type": "array",
"items": {
"type": "string"
}
},
"status": {
"type": "string",
"enum": [
"ACTIVE",
"DISABLED"
]
},
"secret_prefix": {
"type": "string"
},
"consecutive_failures": {
"type": "integer"
},
"last_delivery_at": {
"type": "string",
"nullable": true
},
"created_at": {
"type": "string"
}
}
}{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"webhook_delivery"
]
},
"endpoint_id": {
"type": "string"
},
"event_id": {
"type": "string"
},
"event_type": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"PENDING",
"DELIVERED",
"FAILED",
"DEAD"
]
},
"attempts": {
"type": "integer"
},
"next_attempt_at": {
"type": "string",
"nullable": true
},
"last_status": {
"type": "integer",
"nullable": true
},
"last_error": {
"type": "string",
"nullable": true
},
"delivered_at": {
"type": "string",
"nullable": true
},
"created_at": {
"type": "string"
}
}
}{
"type": "object",
"description": "The body of every webhook delivery, signed with `Taxpio-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">`.",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"event"
]
},
"type": {
"type": "string",
"enum": [
"calculation.completed",
"key.revoked",
"webhook.test"
]
},
"created": {
"type": "string"
},
"environment": {
"type": "string"
},
"workspace_id": {
"type": "string"
},
"data": {
"type": "object"
},
"attempt": {
"type": "integer"
}
}
}{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"connection"
]
},
"environment": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"INVITED",
"ACTIVE",
"REVOKED",
"DECLINED",
"EXPIRED"
]
},
"category": {
"type": "string"
},
"system_name": {
"type": "string"
},
"permissions": {
"type": "array",
"items": {
"type": "string"
}
},
"client_email": {
"type": "string",
"nullable": true
},
"client_name": {
"type": "string",
"nullable": true
},
"profile": {
"type": "object",
"nullable": true,
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"granted_at": {
"type": "string",
"nullable": true
},
"revoked_at": {
"type": "string",
"nullable": true
},
"expires_at": {
"type": "string",
"nullable": true
},
"last_import_at": {
"type": "string",
"nullable": true
},
"records_imported": {
"type": "integer"
},
"documents_imported": {
"type": "integer"
},
"created_at": {
"type": "string"
}
}
}{
"type": "object",
"required": [
"external_id",
"occurred_at",
"description",
"amount"
],
"properties": {
"external_id": {
"type": "string",
"maxLength": 120,
"description": "Your stable id for the record."
},
"occurred_at": {
"type": "string",
"format": "date"
},
"description": {
"type": "string",
"maxLength": 240
},
"amount": {
"type": "string",
"description": "Signed decimal string, at most two decimals, non-zero."
},
"counterparty": {
"type": "string",
"maxLength": 200,
"description": "Folded into the description when the description does not already name it; TaxPio's own counterparty is set when the owner reviews the record."
},
"category": {
"type": "string",
"maxLength": 80,
"description": "Kept as a hint in the import's audit trail; TaxPio's category and tax treatment are set when the owner reviews the record."
}
}
}{
"type": "object",
"properties": {
"object": {
"type": "string",
"enum": [
"principal"
]
},
"workspace": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"application": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"environment": {
"type": "string",
"enum": [
"live",
"sandbox"
]
},
"scopes": {
"type": "array",
"items": {
"type": "string",
"enum": [
"calculations:read",
"calculations:write",
"connections:manage",
"records:import",
"records:read",
"documents:read",
"documents:write",
"webhooks:manage",
"usage:read"
]
}
},
"rate_limit_per_minute": {
"type": "integer"
}
}
}