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