Billing — Automation Rules
Automation rules define when and how invoices are generated automatically when a service reaches a certain status (e.g., FIN — Finished). These endpoints allow you to inspect rules, simulate their evaluation, and trigger automation manually.
All endpoints on this page require a valid JWT token, API key, and tenant header. See Authentication for details.
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.
List Automation Rules
Retrieve all automation rules configured for the tenant.
/apidev/v1/billing/automation-rulesRequest Headers
Every request to a protected endpoint requires these headers:
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token obtained from the Login endpoint. Format: Bearer <token> |
X-API-Key | Yes | Company integration key provided during onboarding. Format: gtk_xxx... |
tenant | Yes | Your company hostname (e.g., yourcompany.geotareas.com) |
Content-Type | Conditional | application/json — required for POST and PUT requests |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: ACTIVE or INACTIVE (default: ACTIVE) |
type | string | Filter by invoice type: PAGAR or COBRAR |
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Rule unique identifier |
name | string | Rule name |
type | string | Invoice type: PAGAR or COBRAR |
status | string | ACTIVE or INACTIVE |
currency.id | string | Currency ID |
currency.name | string | Currency name |
trigger_status.id | string | Trigger status ID |
trigger_status.name | string | Service status that triggers the rule (e.g., "Finalizado") |
initial_invoice_status.id | string | Initial invoice status ID |
initial_invoice_status.name | string | Initial status assigned to generated invoices |
tariff | object | null | Fixed tariff, or null if auto-selected |
auto_select_tariff | boolean | Whether the system auto-selects the best matching tariff |
impute_to | object | Imputation flags (client, account, provider, vehicle, personal, origin) |
concepts_count | integer | Number of concepts in the rule |
filters | object | Summary of active filters with counts and geographic filters |
Code Examples
- cURL
- JavaScript
curl -s -X GET "https://api.example.com/apidev/v1/billing/automation-rules?status=ACTIVE" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const response = await fetch(
'https://api.example.com/apidev/v1/billing/automation-rules?status=ACTIVE',
{
headers: {
'Authorization': `Bearer ${TOKEN}`,
'X-API-Key': APIKEY,
'tenant': TENANT,
},
}
);
const { data } = await response.json();
console.log(`${data.length} active rules found`);
Response Example
{
"success": true,
"data": [
{
"id": "8900123456789",
"name": "TARIFA UNICA PRESTADORES",
"type": "PAGAR",
"status": "ACTIVE",
"currency": { "id": "1", "name": "Pesos Uy" },
"trigger_status": { "id": "5", "name": "Finalizado" },
"initial_invoice_status": { "id": "1", "name": "Pendiente" },
"tariff": null,
"auto_select_tariff": true,
"impute_to": {
"client": false,
"account": false,
"provider": true,
"vehicle": false,
"personal": false,
"origin": true
},
"concepts_count": 2,
"filters": {
"providers_count": 0,
"origins_count": 0,
"prestations_count": 0,
"motives_count": 0,
"vehicles_count": 0,
"personal_count": 0,
"geographic_origin": null,
"geographic_destination": null
}
}
]
}
Automation Rule Detail
Get the full detail of an automation rule including all its filters and concept definitions.
/apidev/v1/billing/automation-rules/{id}Response Example
{
"success": true,
"data": {
"id": "8900123456789",
"name": "TARIFA UNICA PRESTADORES",
"type": "PAGAR",
"status": "ACTIVE",
"currency": { "id": "1", "name": "Pesos Uy" },
"trigger_status": { "id": "5", "name": "Finalizado" },
"initial_invoice_status": { "id": "1", "name": "Pendiente" },
"tariff": null,
"auto_select_tariff": true,
"impute_to": { "client": false, "account": false, "provider": true, "vehicle": false, "personal": false, "origin": true },
"client_id": null,
"account_id": null,
"geographic_filter": {
"origin": { "country_id": null, "department_id": null, "city_id": null, "zone_id": null, "special_place_id": null },
"destination": { "country_id": null, "department_id": null, "city_id": null, "zone_id": null, "special_place_id": null }
},
"filters": {
"providers": [],
"origins": [],
"prestations": [],
"motives": [],
"vehicles": [],
"personal": []
},
"concepts": [
{
"concept_id": "3",
"concept_name": "Movida",
"quantity_type": "FIXED",
"quantity": 1.00,
"price_type": "TARIFF",
"price": null,
"condition_formula": null
},
{
"concept_id": "5",
"concept_name": "Kilometros recorridos",
"quantity_type": "FORMULA",
"quantity_formula": { "id": "101", "name": "Condicion Km Extra" },
"price_type": "TARIFF",
"price": null,
"condition_formula": { "id": "100", "name": "Condicion Km Extra" }
}
]
}
}
Evaluate Rule (Dry Run)
Simulate how an automation rule would apply to a specific service, without creating any invoice. Useful for debugging, QA, and pre-flight checks.
/apidev/v1/billing/automation-rules/{id}/evaluateRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
service_id | string | Yes | Service/task ID to evaluate against |
{ "service_id": "103878" }
Response — Rule Applies
{
"success": true,
"data": {
"rule_id": "8900123456789",
"service_id": "103878",
"applies": true,
"reason": "All filters match",
"tariff_found": {
"id": "1",
"name": "Tarifa General - Prestador",
"selection_level": "GENERAL"
},
"concepts_evaluated": [
{
"concept_id": "3",
"concept_name": "Movida",
"quantity": 1.00,
"unit_price": 900.00,
"subtotal": 900.00,
"tax_percent": 22.00,
"tax_amount": 198.00,
"total": 1098.00,
"quantity_source": "FIXED",
"price_source": "TARIFF"
},
{
"concept_id": "5",
"concept_name": "Kilometros recorridos",
"quantity": 15.0,
"unit_price": 50.00,
"subtotal": 750.00,
"tax_percent": 22.00,
"tax_amount": 165.00,
"total": 915.00,
"quantity_source": "FORMULA",
"formula_detail": {
"formula_name": "Condicion Km Extra",
"condition_passed": true,
"condition_detail": "DISTANCIA_KMS(Base proveedor -> Origen tarea) = 35.0 > 20 -> 35.0 - 20 = 15.0 km"
},
"price_source": "TARIFF"
}
],
"totals": { "subtotal": 1650.00, "tax": 363.00, "total": 2013.00 },
"filters_evaluation": {
"geographic_origin": { "passed": true, "detail": "No geographic filter configured" },
"geographic_destination": { "passed": true, "detail": "No geographic filter configured" },
"providers": { "passed": true, "detail": "No provider filter configured" },
"origins": { "passed": true, "detail": "No origin filter configured" },
"prestations": { "passed": true, "detail": "No presta filter configured" },
"motives": { "passed": true, "detail": "No motive filter configured" },
"vehicles": { "passed": true, "detail": "No vehicle filter configured" },
"personal": { "passed": true, "detail": "No personal filter configured" }
}
}
}
Response — Rule Does Not Apply
{
"success": true,
"data": {
"rule_id": "8900123456789",
"service_id": "103878",
"applies": false,
"reason": "Origin filter mismatch: service.origin='Directa' is not in [Servicios Sura]",
"concepts_evaluated": [],
"totals": null
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
BILLING_AUTOMATION_NOT_FOUND | 404 | Automation rule not found |
BILLING_SERVICE_NOT_FOUND | 404 | Service does not exist |
Run Automation
Evaluate all active automation rules for a specific service and generate the corresponding invoices. This is the same process that runs automatically when a service reaches the trigger status.
/apidev/v1/billing/invoices/automateRequest Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
service_id | string | Yes | — | Service/task ID to automate |
async | boolean | No | false | If true, returns job_id immediately without waiting |
dry_run | boolean | No | false | If true, evaluates all rules without saving any invoice |
{
"service_id": "103878",
"async": false,
"dry_run": false
}
Response — 200 OK (synchronous)
{
"success": true,
"data": {
"service_id": "103878",
"dry_run": false,
"rules_evaluated": 3,
"rules_applied": 1,
"invoices_created": [
{
"id": "7234567890123456789",
"rule_id": "8900123456789",
"rule_name": "TARIFA UNICA PRESTADORES",
"total": 1098.00
}
],
"rules_not_applied": [
{
"rule_id": "8900123456790",
"rule_name": "Regla Solo Montevideo",
"reason": "Geographic filter mismatch"
}
]
}
}
Response — 202 Accepted (async mode)
{
"success": true,
"data": {
"job_id": "job_abc123",
"service_id": "103878",
"status": "QUEUED",
"estimated_completion": "2026-04-03T16:00:05"
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
BILLING_SERVICE_NOT_FOUND | 404 | Service does not exist |
BILLING_AUTOMATION_ALREADY_RAN | 409 | Automation already ran for this service (use force: true to override) |
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | 400 | Request body or query parameters failed validation | Check the error.details field for specific validation failures |
UNAUTHORIZED | 401 | Missing, expired, or invalid JWT token or API key | Re-authenticate via Login to get a fresh token |
RATE_LIMITED | 429 | Too many requests — rate limit exceeded | Wait until the Retry-After header time elapses. See Rate Limits |
INTERNAL_ERROR | 500 | Unexpected server error | Retry after a brief delay. If persistent, contact support |