Transactions
Submit and query fiscalization transactions. All endpoints require authentication.
FiscalAPI never blocks the cash register. POST /v1/transactions always returns 201 Created — fiscalization happens inline when possible, and retries asynchronously on failure.
Create a transaction
POST /v1/transactions
Submits a transaction for fiscalization. The transaction is stored immediately and fiscalized inline when possible. If inline fiscalization fails, it retries asynchronously — the response is always 201.
Request headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token (fsk_test_... or fsk_live_...) |
Content-Type | Yes | application/json |
Idempotency-Key | No | UUID for replay protection. Auto-generated if omitted. |
X-Fiscalization-Timeout | No | Timeout hint in seconds for inline fiscalization |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | UUID of the merchant location |
timestamp | string | Yes | ISO 8601 transaction timestamp |
items | array | Yes | Non-empty array of line items |
pretax_amount | number | Yes | Pre-tax amount (>= 0) |
tax_amount | number | Yes | Tax amount (>= 0) |
total_amount | number | Yes | Total amount (> 0). Must equal pretax_amount + tax_amount |
currency | string | Yes | ISO 4217 currency code (3 letters, e.g. EUR) |
payment_method | string | Yes | One of: cash, card, transfer, other |
customer_context | object | No | Optional customer data for the fiscal receipt |
Examples
curl:
curl -X POST https://api.fiscalapi.com/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer fsk_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": 2.50, "tax_rate": 0.21},
{"description": "Tostada", "quantity": 1, "unit_price": 3.00, "tax_rate": 0.21}
],
"pretax_amount": 6.61,
"tax_amount": 1.39,
"total_amount": 8.00,
"currency": "EUR",
"payment_method": "card"
}'
Python:
import requests
response = requests.post(
"https://api.fiscalapi.com/v1/transactions",
headers={
"Authorization": "Bearer fsk_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": 2.50, "tax_rate": 0.21},
{"description": "Tostada", "quantity": 1, "unit_price": 3.00, "tax_rate": 0.21},
],
"pretax_amount": 6.61,
"tax_amount": 1.39,
"total_amount": 8.00,
"currency": "EUR",
"payment_method": "card",
},
)
transaction = response.json()
print(transaction["id"], transaction["status"])
JavaScript (Node.js):
const response = await fetch("https://api.fiscalapi.com/v1/transactions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer fsk_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: 2.50, tax_rate: 0.21 },
{ description: "Tostada", quantity: 1, unit_price: 3.00, tax_rate: 0.21 },
],
pretax_amount: 6.61,
tax_amount: 1.39,
total_amount: 8.00,
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": 2.50, "tax_rate": 0.21},
{"description": "Tostada", "quantity": 1, "unit_price": 3.00, "tax_rate": 0.21},
},
"pretax_amount": 6.61,
"tax_amount": 1.39,
"total_amount": 8.00,
"currency": "EUR",
"payment_method": "card",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.fiscalapi.com/v1/transactions", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer fsk_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"
}
The amount field is stored in cents (integer). A total_amount of 8.00 becomes 800.
Errors
| Status | Error | Cause |
|---|---|---|
400 | Key: 'CreateRequest.LocationID'... | Missing required field |
400 | items must be a non-empty array | Empty or invalid items array |
400 | pretax_amount + tax_amount must equal total_amount | Amount mismatch (tolerance: 0.01) |
400 | location not found | Invalid location_id or not owned by account |
400 | location is not active | Location 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
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Items per page (max 100) |
offset | integer | 0 | Pagination offset (max 10000) |
status | string | -- | Filter by status: pending, processing, success, failed, failed_permanent, pending_lroe, cancelled |
Examples
curl:
# List all transactions
curl https://api.fiscalapi.com/v1/transactions \
-H "Authorization: Bearer fsk_test_abc123def456..."
# Filter by status and paginate
curl "https://api.fiscalapi.com/v1/transactions?status=success&limit=10&offset=0" \
-H "Authorization: Bearer fsk_test_abc123def456..."
Python:
import requests
response = requests.get(
"https://api.fiscalapi.com/v1/transactions",
headers={"Authorization": "Bearer fsk_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.fiscalapi.com/v1/transactions?${params}`,
{ headers: { "Authorization": "Bearer fsk_test_abc123def456..." } },
);
const { data, total } = await response.json();
console.log(`${total} transactions found`);
Go:
req, _ := http.NewRequest("GET", "https://api.fiscalapi.com/v1/transactions?status=success&limit=10", nil)
req.Header.Set("Authorization", "Bearer fsk_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
| Parameter | Type | Description |
|---|---|---|
id | uuid | Transaction UUID |
Examples
curl:
curl https://api.fiscalapi.com/v1/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer fsk_test_abc123def456..."
Python:
import requests
tx_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
response = requests.get(
f"https://api.fiscalapi.com/v1/transactions/{tx_id}",
headers={"Authorization": "Bearer fsk_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.fiscalapi.com/v1/transactions/${txId}`,
{ headers: { "Authorization": "Bearer fsk_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.fiscalapi.com/v1/transactions/"+txID, nil)
req.Header.Set("Authorization", "Bearer fsk_test_abc123def456...")
resp, err := http.DefaultClient.Do(req)
Response 200 OK
Returns a Transaction object.
Errors
| Status | Error |
|---|---|
404 | transaction 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
| Parameter | Type | Description |
|---|---|---|
id | uuid | Transaction UUID to cancel |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | No | Cancellation reason |
Example
curl -X POST https://api.fiscalapi.com/v1/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890/cancel \
-H "Content-Type: application/json" \
-H "Authorization: Bearer fsk_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:
| Status | Cancellable |
|---|---|
success | Yes |
pending_lroe | Yes |
pending_submission | Yes |
pending | No |
processing | No |
failed | No |
failed_permanent | No |
cancelled | No |
Errors
| Status | Error | Cause |
|---|---|---|
404 | transaction not found | Invalid ID |
409 | transaction cannot be cancelled | Transaction is not in a cancellable status |
Idempotency
Cancellation is idempotent. Cancelling an already-cancelled transaction returns the existing cancellation transaction.
Transaction object
| Field | Type | Description |
|---|---|---|
id | uuid | Transaction identifier |
account_id | uuid | Owning account identifier |
location_id | uuid | Merchant location identifier |
idempotency_key | string | Idempotency key for replay protection |
type | string | Transaction type: sale or cancellation |
status | string | Fiscalization status (see below) |
amount | integer | Total amount in cents |
currency | string | ISO 4217 currency code |
country | string | ISO 3166-1 alpha-2 country code |
request_payload | object | Original request body (stored for audit) |
response_payload | object | Fiscal authority response (when available) |
fiscal_id | string | Fiscal identifier assigned by the tax authority |
environment | string | test or live (determined by API key prefix) |
error_message | string | Error details (when status is failed) |
submitted_at | datetime | When the transaction was sent to the fiscal authority |
completed_at | datetime | When fiscalization completed |
created_at | datetime | Creation timestamp |
Transaction statuses
| Status | Description |
|---|---|
pending | Created, awaiting fiscalization |
processing | Being processed by the fiscal authority |
success | Fiscalized successfully -- fiscal_id is set |
failed | Fiscalization failed -- see error_message. Will be retried. |
failed_permanent | Fiscalization permanently failed (max retries exceeded, moved to dead letter) |
pending_lroe | Inline fiscalization succeeded, awaiting LROE batch submission (Spain TicketBAI) |
cancelled | Transaction was cancelled via the cancel endpoint |