Skip to main content

Transactions

Submit and query fiscalization transactions. All endpoints require authentication.

info

Zyntem never blocks the cash register. POST /v1/transactions always returns 201 Created with the fiscal ID and receipt data populated immediately. Submission to the tax authority happens automatically in the background.

Create a transaction

POST /v1/transactions

Submits a transaction for fiscalization. The prepare phase runs synchronously (no network calls) and populates fiscal_id and receipt data immediately. Submission to the tax authority happens automatically in the background. The response is always 201.

Request headers

HeaderRequiredDescription
AuthorizationYesBearer token (zyn_test_... or zyn_live_...)
Content-TypeYesapplication/json
Idempotency-KeyNoUUID for replay protection. Auto-generated if omitted.
X-Fiscalization-TimeoutNoTimeout hint in seconds for inline fiscalization

Request body

FieldTypeRequiredDescription
location_idstringYesUUID of the merchant location
timestampstringYesISO 8601 transaction timestamp
itemsarrayYesNon-empty array of line items
pretax_amountinteger (int64)YesPre-tax amount in minor units, e.g. cents (>= 0)
tax_amountinteger (int64)YesTax amount in minor units (>= 0)
total_amountinteger (int64)YesTotal amount in minor units (> 0). Must equal pretax_amount + tax_amount exactly
currencystringYesISO 4217 currency code (3 letters, e.g. EUR)
payment_methodstringYesOne of: cash, card, transfer, other
customer_contextobjectNoOptional customer data for the fiscal receipt

Examples

curl:

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"location_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"timestamp": "2026-03-09T14:30:00Z",
"items": [
{"description": "Café con leche", "quantity": 2, "unit_price": 250, "tax_rate": 21.0},
{"description": "Tostada", "quantity": 1, "unit_price": 300, "tax_rate": 21.0}
],
"pretax_amount": 661,
"tax_amount": 139,
"total_amount": 800,
"currency": "EUR",
"payment_method": "card"
}'

Python:

import requests

response = requests.post(
"https://api.zyntem.dev/v1/transactions",
headers={
"Authorization": "Bearer zyn_test_abc123def456...",
"Idempotency-Key": "550e8400-e29b-41d4-a716-446655440000",
},
json={
"location_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"timestamp": "2026-03-09T14:30:00Z",
"items": [
{"description": "Café con leche", "quantity": 2, "unit_price": 250, "tax_rate": 21.0},
{"description": "Tostada", "quantity": 1, "unit_price": 300, "tax_rate": 21.0},
],
"pretax_amount": 661,
"tax_amount": 139,
"total_amount": 800,
"currency": "EUR",
"payment_method": "card",
},
)
transaction = response.json()
print(transaction["id"], transaction["status"])

JavaScript (Node.js):

const response = await fetch("https://api.zyntem.dev/v1/transactions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer zyn_test_abc123def456...",
"Idempotency-Key": "550e8400-e29b-41d4-a716-446655440000",
},
body: JSON.stringify({
location_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
timestamp: "2026-03-09T14:30:00Z",
items: [
{ description: "Café con leche", quantity: 2, unit_price: 250, tax_rate: 21.0 },
{ description: "Tostada", quantity: 1, unit_price: 300, tax_rate: 21.0 },
],
pretax_amount: 661,
tax_amount: 139,
total_amount: 800,
currency: "EUR",
payment_method: "card",
}),
});
const transaction = await response.json();
console.log(transaction.id, transaction.status);

Go:

payload := map[string]interface{}{
"location_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"timestamp": "2026-03-09T14:30:00Z",
"items": []map[string]interface{}{
{"description": "Café con leche", "quantity": 2, "unit_price": 250, "tax_rate": 21.0},
{"description": "Tostada", "quantity": 1, "unit_price": 300, "tax_rate": 21.0},
},
"pretax_amount": 661,
"tax_amount": 139,
"total_amount": 800,
"currency": "EUR",
"payment_method": "card",
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.zyntem.dev/v1/transactions", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer zyn_test_abc123def456...")
req.Header.Set("Idempotency-Key", "550e8400-e29b-41d4-a716-446655440000")

resp, err := http.DefaultClient.Do(req)

Response 201 Created

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"account_id": "9f8e7d6c-5b4a-3210-fedc-ba0987654321",
"location_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"type": "sale",
"status": "pending",
"amount": 800,
"currency": "EUR",
"country": "ES",
"request_payload": { "...": "original request body" },
"fiscal_id": null,
"created_at": "2026-03-09T14:30:01Z"
}
info

All money fields (pretax_amount, tax_amount, total_amount, unit_price, amount) are integers in minor units (cents for EUR). A €8.00 sale is sent and returned as 800.

Errors

StatusErrorCause
400Key: 'CreateRequest.LocationID'...Missing required field
400items must be a non-empty arrayEmpty or invalid items array
400pretax_amount + tax_amount must equal total_amountAmount mismatch (exact equality required since values are integer cents)
400location not foundInvalid location_id or not owned by account
400location is not activeLocation has been deactivated

Idempotency

Include an Idempotency-Key header to safely retry requests. If a transaction with the same key already exists for your account, the existing transaction is returned without creating a duplicate.


List transactions

GET /v1/transactions

Returns a paginated list of transactions for the authenticated account, ordered by created_at descending.

Query parameters

ParameterTypeDefaultDescription
limitinteger20Items per page (max 100)
offsetinteger0Pagination offset (max 10000)
statusstring--Filter by status: pending, processing, success, failed, failed_permanent, pending_batch, cancelled

Examples

curl:

# List all transactions
curl https://api.zyntem.dev/v1/transactions \
-H "Authorization: Bearer zyn_test_abc123def456..."

# Filter by status and paginate
curl "https://api.zyntem.dev/v1/transactions?status=success&limit=10&offset=0" \
-H "Authorization: Bearer zyn_test_abc123def456..."

Python:

import requests

response = requests.get(
"https://api.zyntem.dev/v1/transactions",
headers={"Authorization": "Bearer zyn_test_abc123def456..."},
params={"status": "success", "limit": 10},
)
data = response.json()
for tx in data["data"]:
print(tx["id"], tx["status"], tx["amount"])

JavaScript (Node.js):

const params = new URLSearchParams({ status: "success", limit: "10" });
const response = await fetch(
`https://api.zyntem.dev/v1/transactions?${params}`,
{ headers: { "Authorization": "Bearer zyn_test_abc123def456..." } },
);
const { data, total } = await response.json();
console.log(`${total} transactions found`);

Go:

req, _ := http.NewRequest("GET", "https://api.zyntem.dev/v1/transactions?status=success&limit=10", nil)
req.Header.Set("Authorization", "Bearer zyn_test_abc123def456...")
resp, err := http.DefaultClient.Do(req)

Response 200 OK

{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"account_id": "9f8e7d6c-5b4a-3210-fedc-ba0987654321",
"location_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
"type": "sale",
"status": "success",
"amount": 800,
"currency": "EUR",
"country": "ES",
"request_payload": { "...": "original request body" },
"fiscal_id": "TBAI-12345678",
"created_at": "2026-03-09T14:30:01Z",
"submitted_at": "2026-03-09T14:30:02Z",
"completed_at": "2026-03-09T14:30:03Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}

Get a transaction

GET /v1/transactions/{id}

Returns a single transaction by ID, scoped to the authenticated account. Use this to poll for fiscalization status.

Path parameters

ParameterTypeDescription
iduuidTransaction UUID

Examples

curl:

curl https://api.zyntem.dev/v1/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer zyn_test_abc123def456..."

Python:

import requests

tx_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
response = requests.get(
f"https://api.zyntem.dev/v1/transactions/{tx_id}",
headers={"Authorization": "Bearer zyn_test_abc123def456..."},
)
tx = response.json()
print(tx["status"], tx.get("fiscal_id"))

JavaScript (Node.js):

const txId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
const response = await fetch(
`https://api.zyntem.dev/v1/transactions/${txId}`,
{ headers: { "Authorization": "Bearer zyn_test_abc123def456..." } },
);
const tx = await response.json();
console.log(tx.status, tx.fiscal_id);

Go:

txID := "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
req, _ := http.NewRequest("GET", "https://api.zyntem.dev/v1/transactions/"+txID, nil)
req.Header.Set("Authorization", "Bearer zyn_test_abc123def456...")
resp, err := http.DefaultClient.Do(req)

Response 200 OK

Returns a Transaction object.

Errors

StatusError
404transaction not found

Cancel a transaction

POST /v1/transactions/{id}/cancel

Cancels a previously fiscalized transaction. A cancellation transaction is created and submitted to the fiscal authority.

Path parameters

ParameterTypeDescription
iduuidTransaction UUID to cancel

Request body

FieldTypeRequiredDescription
reasonstringNoCancellation reason

Example

curl -X POST https://api.zyntem.dev/v1/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/cancel \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{"reason": "Customer returned items"}'

Response 201 Created

Returns a new transaction with type: "cancellation":

{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"type": "cancellation",
"status": "pending",
"amount": 800,
"currency": "EUR",
"environment": "test",
"created_at": "2026-03-09T15:00:00Z"
}

Cancellable statuses

Only transactions in certain statuses can be cancelled:

StatusCancellable
successYes
pending_batchYes
pending_submissionYes
pendingNo
processingNo
failedNo
failed_permanentNo
cancelledNo

Errors

StatusErrorCause
404transaction not foundInvalid ID
409transaction cannot be cancelledTransaction is not in a cancellable status

Idempotency

Cancellation is idempotent. Cancelling an already-cancelled transaction returns the existing cancellation transaction.


Fiscalize a transaction

POST /v1/transactions/{id}/fiscalize

Submits a pending_submission or pending transaction to the tax authority. In the two-phase model, POST /v1/transactions calls prepare() synchronously and populates the fiscal ID and receipt data immediately. This endpoint takes the prepared record and submits it to the tax authority.

For legacy pending transactions (created before the two-phase model), falls back to the full process_transaction flow.

Path parameters

ParameterTypeDescription
iduuidTransaction UUID to fiscalize

Example

curl -X POST https://api.zyntem.dev/v1/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/fiscalize \
-H "Authorization: Bearer zyn_test_abc123def456..."

Response 200 OK

Returns the updated Transaction object with status: "success" and the fiscal authority response in response_payload.

Errors

StatusErrorCause
404transaction not foundInvalid ID
409transaction cannot be fiscalized (current status: ...)Transaction is not in a fiscalizable status (pending_submission or pending)

Transaction object

FieldTypeDescription
iduuidTransaction identifier
account_iduuidOwning account identifier
location_iduuidMerchant location identifier
idempotency_keystringIdempotency key for replay protection
typestringTransaction type: sale or cancellation
statusstringFiscalization status (see below)
amountintegerTotal amount in cents
currencystringISO 4217 currency code
countrystringISO 3166-1 alpha-2 country code
request_payloadobjectOriginal request body (stored for audit)
response_payloadobjectFiscal authority response (when available)
fiscal_idstringFiscal identifier assigned by the tax authority
environmentstringtest or live (determined by API key prefix)
error_messagestringError details (when status is failed)
submitted_atdatetimeWhen the transaction was sent to the fiscal authority
completed_atdatetimeWhen fiscalization completed
created_atdatetimeCreation timestamp

Transaction statuses

StatusDescription
pendingCreated, awaiting fiscalization
processingBeing processed by the fiscal authority
successFiscalized successfully -- fiscal_id is set
failedFiscalization failed -- see error_message. Will be retried.
failed_permanentFiscalization permanently failed (max retries exceeded, moved to dead letter)
pending_batchInline fiscalization succeeded, awaiting batch submission. Response includes next_batch_at, batch_system, and batch_frequency fields.
cancelledTransaction was cancelled via the cancel endpoint