Skip to main content

Billing — Invoices (Write)

Create, update, and change the status of invoices in the GeoTareas billing system.

Prerequisites

All endpoints on this page require a valid JWT token, API key, and tenant header. See Authentication for details.

Pending Implementation

These endpoints are documented but not yet implemented in the public API (/apidev/v1/billing/*). The internal billing services exist in geotareas/facturacion/ but have not been wired to the API developers module. This documentation reflects the planned contract — DTOs, controller routes, permissions, and tests are still pending.


Create Invoice

Create an invoice manually (without automation). Requires a valid service ID and at least one concept.

POST/apidev/v1/billing/invoices
PermissionAPICLI_BILLING_WRITE
Rate Limit20 req/min (sliding window)
CacheNone

Request Headers

Every request to a protected endpoint requires these headers:

HeaderRequiredDescription
AuthorizationYesBearer token obtained from the Login endpoint. Format: Bearer <token>
X-API-KeyYesCompany integration key provided during onboarding. Format: gtk_xxx...
tenantYesYour company hostname (e.g., yourcompany.geotareas.com)
Content-TypeConditionalapplication/json — required for POST and PUT requests

Request Body

FieldTypeRequiredDescription
service_idstringYesService/task ID
typestringYesInvoice type: PAGAR or COBRAR
currency_idstringYesCurrency ID (see Catalogs)
tariff_idstringNoTariff ID. If omitted, prices in concepts are used directly
number_manualstringNoCustom invoice number
datestringNoInvoice date ISO 8601 (default: now)
notesstringNoObservations (max 2000 chars)
impute_toobjectNoImputation flags
impute_to.providerbooleanNoImpute to provider
impute_to.accountbooleanNoImpute to account
impute_to.originbooleanNoImpute to origin
conceptsarrayYesAt least one concept line
concepts[].concept_idstringYesConcept ID (see Catalogs)
concepts[].quantitynumberYesQuantity (must be > 0)
concepts[].unit_pricenumberYesUnit price
concepts[].tax_percentnumberNoTax percentage (default: 0)

Code Examples

curl -s -X POST "https://api.example.com/apidev/v1/billing/invoices" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"service_id": "103878",
"type": "PAGAR",
"currency_id": "1",
"tariff_id": "1",
"number_manual": "001-0001",
"date": "2026-04-03T15:02:00",
"notes": "Manual invoice",
"impute_to": { "provider": true, "origin": true },
"concepts": [
{
"concept_id": "3",
"quantity": 1.00,
"unit_price": 900.00,
"tax_percent": 22.00
}
]
}'

Response — 201 Created

{
"success": true,
"data": {
"id": "7234567890123456789",
"subtotal": 900.00,
"tax": 198.00,
"total": 1098.00,
"status": { "code": "PENDIENTE" }
}
}

Errors

CodeHTTPDescription
BILLING_SERVICE_NOT_FOUND404Service does not exist or belongs to another tenant
BILLING_INVOICE_ALREADY_EXISTS409An invoice already exists for this service with the same type
BILLING_TARIFF_INVALID422Tariff does not apply to this service
BILLING_CONCEPT_NOT_FOUND422concept_id does not exist
CodeHTTP StatusDescriptionResolution
VALIDATION_ERROR400Request body or query parameters failed validationCheck the error.details field for specific validation failures
UNAUTHORIZED401Missing, expired, or invalid JWT token or API keyRe-authenticate via Login to get a fresh token
RATE_LIMITED429Too many requests — rate limit exceededWait until the Retry-After header time elapses. See Rate Limits
INTERNAL_ERROR500Unexpected server errorRetry after a brief delay. If persistent, contact support

Update Invoice

Update editable fields of an invoice in PENDIENTE status. Does not allow changing concepts — use Replace Concepts for that.

PUT/apidev/v1/billing/invoices/{id}
PermissionAPICLI_BILLING_WRITE
Rate Limit20 req/min

Request Body (all fields optional)

FieldTypeDescription
number_manualstringUpdated invoice number
datestringUpdated invoice date (ISO 8601)
notesstringUpdated observations
impute_toobjectUpdated imputation flags
{
"number_manual": "001-0002",
"date": "2026-04-03T16:00:00",
"notes": "Corrected invoice",
"impute_to": {
"provider": true,
"vehicle": true
}
}

Errors

CodeHTTPDescription
BILLING_INVOICE_NOT_FOUND404Invoice not found
BILLING_INVOICE_NOT_EDITABLE422Invoice is in a non-editable status (Closed, Cancelled)

Replace Concepts

Replace all line items of an invoice. Totals are automatically recalculated. Only works on invoices in PENDIENTE status.

PUT/apidev/v1/billing/invoices/{id}/concepts
PermissionAPICLI_BILLING_WRITE
Rate Limit20 req/min
Full replacement

This endpoint replaces all existing concepts. It is not a partial update — send the complete list of concepts you want the invoice to have.

Request Body

{
"concepts": [
{
"concept_id": "3",
"quantity": 1.00,
"unit_price": 950.00,
"tax_percent": 22.00
},
{
"concept_id": "5",
"quantity": 15.00,
"unit_price": 50.00,
"tax_percent": 22.00
}
]
}

Response — 200 OK

{
"success": true,
"data": {
"invoice_id": "7234567890123456789",
"subtotal": 1700.00,
"tax": 374.00,
"total": 2074.00,
"concepts_count": 2
}
}

Change Invoice Status

Change the status of an invoice. Only valid transitions are allowed.

PUT/apidev/v1/billing/invoices/{id}/status
PermissionAPICLI_BILLING_ADMIN
Rate Limit20 req/min

Request Body

FieldTypeRequiredDescription
status_codestringYesTarget status code (see Billing Statuses)
notesstringNoReason for the status change
{
"status_code": "CONCILIADO",
"notes": "Reconciled with settlement #456"
}

Response — 200 OK

{
"success": true,
"data": {
"invoice_id": "7234567890123456789",
"previous_status": "PENDIENTE",
"new_status": "CONCILIADO",
"changed_at": "2026-04-03T16:00:00"
}
}

Errors

CodeHTTPDescription
BILLING_INVOICE_NOT_FOUND404Invoice not found
BILLING_INVALID_STATUS400Target status does not exist
BILLING_INVALID_STATUS_TRANSITION422Transition from current to target status is not allowed
BILLING_INVOICE_LOCKED422Cancelled invoices cannot change status