Supplier Portal — Invoices
These endpoints let a supplier (prestador) manage their own invoices directly from the API: list them, open one for detail, declare the amount they are billing, declare many at once, and check an amount against the company tolerance before sending it. Every endpoint is scoped to the supplier account behind the token — you only ever see and touch your own invoices.
Supplier account required
The authenticated user must be linked to a supplier (prestador). Requests from a non-supplier account are rejected with 403 FORBIDDEN. All endpoints require a valid JWT token, API key, and tenant header. See Authentication .
List Invoices
Retrieve a paginated list of the supplier's invoices for a date range, with optional status and free-text search. Each row carries the system amount, the declared amount, and the difference between them. The response also includes the tolerance that applies to the supplier so you can interpret the differences.
GET /apidev/v1/supplier-portal/invoices
Permission APICLI_PORTALPROVEEDOR_READ
Rate Limit 30 req/min (sliding window)
Every request to a protected endpoint requires these headers:
Header Required Description AuthorizationYes Bearer token obtained from the Login endpoint. Format: Bearer <token> X-API-KeyYes Company integration key provided during onboarding. Format: gtk_xxx... tenantYes Your assigned tenant domain (default: geotareas.com) — always send your assigned tenant Content-TypeConditional application/json — required for POST and PUT requests
Query Parameters
Parameter Type Required Description startdatestring (ISO 8601) Yes Start of the date range enddatestring (ISO 8601) Yes End of the date range (range may not exceed 93 days) statusstring No Filter by invoice state code: PEN, ENV, APR, REV, REC, AJU, LIQ searchstring No Free-text match on service number, account, or invoice number (max 120 characters) limitinteger No Records per page. Min: 1, Max: 100, Default: 25 offsetinteger No Records to skip. Default: 0
Response Fields — data.rows[]
Field Type Description invoice_idstring Invoice unique identifier invoice_datestring | null Invoice date service_idstring Related service/task ID service_numberstring | null Service display number service_call_datestring | null When the service was requested account_namestring | null Account the service belongs to citystring | null Service city system_amountnumber Amount calculated by the system declaration_idstring | null Declaration ID, if the supplier already declared an amount declared_amountnumber | null Amount declared by the supplier declared_notesstring | null Notes the supplier added on the declaration provider_invoice_numberstring | null Supplier's own invoice number provider_invoice_datestring | null Supplier's own invoice date state_codestring Invoice state code (PEN, ENV, …) state_namestring Invoice state name differencenumber | null Declared minus system amount difference_pctnumber | null Difference as a percentage of the system amount attachment_urlstring | null URL to the attached invoice file attachment_filenamestring | null Original file name of the attachment
Response Fields — data.tolerance
Field Type Description modestring | null Tolerance mode: PCT, ABS, AMB, or null when none applies tolerance_pctnumber | null Allowed percentage difference tolerance_absnumber | null Allowed absolute difference auto_approveboolean Whether amounts within tolerance are approved automatically sourcestring Where the tolerance comes from: proveedor, global, or ninguna
Code Example
curl -s "https://$TENANT/apidev/v1/supplier-portal/invoices?startdate=2026-05-01&enddate=2026-05-31&status=PEN&limit=10" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const params = new URLSearchParams ( {
startdate : '2026-05-01' ,
enddate : '2026-05-31' ,
status : 'PEN' ,
limit : '10' ,
} ) ;
const response = await fetch (
` https:// ${ TENANT } /apidev/v1/supplier-portal/invoices? ${ params } ` ,
{
headers : {
Authorization : ` Bearer ${ TOKEN } ` ,
'X-API-Key' : APIKEY ,
tenant : TENANT ,
} ,
}
) ;
const { data , meta } = await response . json ( ) ;
console . log ( ` ${ data . rows . length } of ${ meta . total } invoices, tolerance source: ${ data . tolerance . source } ` ) ;
Example Response
{
"success" : true ,
"data" : {
"rows" : [
{
"invoice_id" : "7234567890123456789" ,
"invoice_date" : "2026-05-03T15:02:00" ,
"service_id" : "103878" ,
"service_number" : "SRV-2026-0042" ,
"service_call_date" : "2026-05-02T09:10:00" ,
"account_name" : "Supermercado Centro" ,
"city" : "Montevideo" ,
"system_amount" : 1098.00 ,
"declaration_id" : "8100000000045" ,
"declared_amount" : 1100.00 ,
"declared_notes" : "Incluye traslado." ,
"provider_invoice_number" : "A-0001-0000123" ,
"provider_invoice_date" : "2026-05-03" ,
"state_code" : "ENV" ,
"state_name" : "Enviada" ,
"difference" : 2.00 ,
"difference_pct" : 0.18 ,
"attachment_url" : "https://files.example.com/inv/123.pdf" ,
"attachment_filename" : "factura-123.pdf"
}
] ,
"tolerance" : {
"mode" : "PCT" ,
"tolerance_pct" : 5.0 ,
"tolerance_abs" : null ,
"auto_approve" : true ,
"source" : "proveedor"
}
} ,
"meta" : {
"total" : 1 ,
"limit" : 10 ,
"offset" : 0
}
}
Invoice Detail
Retrieve a single invoice with its full breakdown: the same row fields as the list, plus the line-item concepts and the tolerance that applies. Returns 404 if the invoice does not exist or does not belong to the supplier.
GET /apidev/v1/supplier-portal/invoices/{id}
Permission APICLI_PORTALPROVEEDOR_READ
Rate Limit 30 req/min (sliding window)
Path Parameters
Parameter Type Required Description idstring Yes Invoice unique identifier
Response Fields — data.concepts[]
Field Type Description concept_idstring Concept catalog ID descriptionstring Concept description unit_amountnumber Amount per unit quantitynumber Quantity tax_pctnumber Tax percentage applied totalnumber Line total
The data object also includes every field listed for a list row and a tolerance object (see List Invoices ).
Code Example
curl -s "https://$TENANT/apidev/v1/supplier-portal/invoices/7234567890123456789" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT"
const response = await fetch (
` https:// ${ TENANT } /apidev/v1/supplier-portal/invoices/7234567890123456789 ` ,
{
headers : {
Authorization : ` Bearer ${ TOKEN } ` ,
'X-API-Key' : APIKEY ,
tenant : TENANT ,
} ,
}
) ;
const { data } = await response . json ( ) ;
console . log ( ` ${ data . service_number } : ${ data . concepts . length } concepts ` ) ;
Example Response
{
"success" : true ,
"data" : {
"invoice_id" : "7234567890123456789" ,
"invoice_date" : "2026-05-03T15:02:00" ,
"service_id" : "103878" ,
"service_number" : "SRV-2026-0042" ,
"account_name" : "Supermercado Centro" ,
"city" : "Montevideo" ,
"system_amount" : 1098.00 ,
"declaration_id" : "8100000000045" ,
"declared_amount" : 1100.00 ,
"declared_notes" : "Incluye traslado." ,
"provider_invoice_number" : "A-0001-0000123" ,
"provider_invoice_date" : "2026-05-03" ,
"state_code" : "ENV" ,
"state_name" : "Enviada" ,
"difference" : 2.00 ,
"difference_pct" : 0.18 ,
"attachment_url" : "https://files.example.com/inv/123.pdf" ,
"attachment_filename" : "factura-123.pdf" ,
"concepts" : [
{
"concept_id" : "100" ,
"description" : "Mano de obra" ,
"unit_amount" : 900.00 ,
"quantity" : 1 ,
"tax_pct" : 22.0 ,
"total" : 1098.00
}
] ,
"tolerance" : {
"mode" : "PCT" ,
"tolerance_pct" : 5.0 ,
"tolerance_abs" : null ,
"auto_approve" : true ,
"source" : "proveedor"
}
} ,
"meta" : { }
}
Declare Invoice Amount
Declare the amount the supplier is billing for an invoice. This is an upsert : if no declaration exists yet it is created, otherwise the existing one is updated. The response reports the resulting state and how the declared amount compares to the company tolerance.
POST /apidev/v1/supplier-portal/invoices/declare
Permission APICLI_PORTALPROVEEDOR_WRITE
Rate Limit 10 req/min (sliding window)
Request Body
Field Type Required Description invoice_idstring Yes Invoice to declare (numeric string) amountnumber Yes Declared amount. Must be >= 0 notesstring No Free-text notes for the declaration invoice_numberstring No Supplier's own invoice number (max 60 characters) invoice_datestring (ISO 8601) No Supplier's own invoice date tax_pctnumber No Tax percentage (0–100)
Response Fields — data.tolerance
Field Type Description within_toleranceboolean Whether the declared amount is within tolerance differencenumber Declared minus system amount difference_pctnumber Difference as a percentage of the system amount difference_absnumber Absolute difference actionstring Resulting action (e.g. auto-approve or send to review) config_sourcestring Where the applied tolerance comes from
Code Example
curl -s -X POST "https://$TENANT/apidev/v1/supplier-portal/invoices/declare" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"invoice_id": "7234567890123456789",
"amount": 1100.00,
"notes": "Incluye traslado.",
"invoice_number": "A-0001-0000123",
"invoice_date": "2026-05-03",
"tax_pct": 22
}'
const response = await fetch (
` https:// ${ TENANT } /apidev/v1/supplier-portal/invoices/declare ` ,
{
method : 'POST' ,
headers : {
Authorization : ` Bearer ${ TOKEN } ` ,
'X-API-Key' : APIKEY ,
tenant : TENANT ,
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( {
invoice_id : '7234567890123456789' ,
amount : 1100.0 ,
notes : 'Incluye traslado.' ,
invoice_number : 'A-0001-0000123' ,
invoice_date : '2026-05-03' ,
tax_pct : 22 ,
} ) ,
}
) ;
const { data } = await response . json ( ) ;
console . log ( ` Declared. Within tolerance: ${ data . tolerance . within_tolerance } ` ) ;
Example Response
{
"success" : true ,
"data" : {
"declaration_id" : "8100000000045" ,
"declared_amount" : 1100.00 ,
"state_id" : 101 ,
"tolerance" : {
"within_tolerance" : true ,
"difference" : 2.00 ,
"difference_pct" : 0.18 ,
"difference_abs" : 2.00 ,
"action" : "AUTO_APPROVE" ,
"config_source" : "proveedor"
}
} ,
"meta" : { }
}
Declare Batch
Declare amounts for many invoices in one request. Each item is processed independently, so the request always returns 200 with a per-item success or failure. Use this to push a whole period's declarations at once.
POST /apidev/v1/supplier-portal/invoices/declare-batch
Permission APICLI_PORTALPROVEEDOR_WRITE
Rate Limit 10 req/min (sliding window)
Request Body
Field Type Required Description declarationsarray Yes List of declarations (1–100 items)
Each item in declarations[] accepts the same fields as Declare Invoice Amount :
Field Type Required Description invoice_idstring Yes Invoice to declare amountnumber Yes Declared amount (>= 0) notesstring No Free-text notes invoice_numberstring No Supplier's own invoice number invoice_datestring (ISO 8601) No Supplier's own invoice date tax_pctnumber No Tax percentage (0–100)
Response Fields — data
Field Type Description resultsarray One entry per submitted item, in order acceptednumber How many items were declared successfully rejectednumber How many items failed
Each results[] entry includes invoice_id and success. On success it carries the same fields as a single declaration; on failure it carries an error describing why.
Code Example
curl -s -X POST "https://$TENANT/apidev/v1/supplier-portal/invoices/declare-batch" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"declarations": [
{ "invoice_id": "7234567890123456789", "amount": 1100.00, "invoice_number": "A-0001-0000123" },
{ "invoice_id": "7234567890123456790", "amount": 540.00 }
]
}'
const response = await fetch (
` https:// ${ TENANT } /apidev/v1/supplier-portal/invoices/declare-batch ` ,
{
method : 'POST' ,
headers : {
Authorization : ` Bearer ${ TOKEN } ` ,
'X-API-Key' : APIKEY ,
tenant : TENANT ,
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( {
declarations : [
{ invoice_id : '7234567890123456789' , amount : 1100.0 , invoice_number : 'A-0001-0000123' } ,
{ invoice_id : '7234567890123456790' , amount : 540.0 } ,
] ,
} ) ,
}
) ;
const { data } = await response . json ( ) ;
console . log ( ` ${ data . accepted } accepted, ${ data . rejected } rejected ` ) ;
Example Response
{
"success" : true ,
"data" : {
"results" : [
{
"invoice_id" : "7234567890123456789" ,
"success" : true ,
"declaration_id" : "8100000000045" ,
"declared_amount" : 1100.00 ,
"state_id" : 101 ,
"tolerance" : {
"within_tolerance" : true ,
"difference" : 2.00 ,
"difference_pct" : 0.18 ,
"difference_abs" : 2.00 ,
"action" : "AUTO_APPROVE" ,
"config_source" : "proveedor"
}
} ,
{
"invoice_id" : "7234567890123456790" ,
"success" : false ,
"error" : {
"code" : "NOT_FOUND" ,
"message" : "Invoice not found or not owned by this supplier."
}
}
] ,
"accepted" : 1 ,
"rejected" : 1
} ,
"meta" : { }
}
Validate Amount (Pre-flight)
Check an amount against the company tolerance before declaring it. This endpoint writes nothing — it only tells you how the amount would be treated. Returns 404 if the invoice does not belong to the supplier.
POST /apidev/v1/supplier-portal/invoices/validate
Permission APICLI_PORTALPROVEEDOR_READ
Rate Limit 30 req/min (sliding window)
Request Body
Field Type Required Description invoice_idstring Yes Invoice to check amountnumber Yes Amount to test. Must be >= 0
Response Fields — data
Field Type Description invoice_idstring Invoice that was checked system_amountnumber Amount calculated by the system declared_amountnumber Amount you submitted for the check within_toleranceboolean Whether the amount is within tolerance differencenumber Submitted minus system amount difference_pctnumber Difference as a percentage of the system amount difference_absnumber Absolute difference actionstring Action that would result (e.g. auto-approve or send to review) config_sourcestring Where the applied tolerance comes from
Code Example
curl -s -X POST "https://$TENANT/apidev/v1/supplier-portal/invoices/validate" \
-H "Authorization: Bearer $TOKEN" \
-H "X-API-Key: $APIKEY" \
-H "tenant: $TENANT" \
-H "Content-Type: application/json" \
-d '{
"invoice_id": "7234567890123456789",
"amount": 1500.00
}'
const response = await fetch (
` https:// ${ TENANT } /apidev/v1/supplier-portal/invoices/validate ` ,
{
method : 'POST' ,
headers : {
Authorization : ` Bearer ${ TOKEN } ` ,
'X-API-Key' : APIKEY ,
tenant : TENANT ,
'Content-Type' : 'application/json' ,
} ,
body : JSON . stringify ( {
invoice_id : '7234567890123456789' ,
amount : 1500.0 ,
} ) ,
}
) ;
const { data } = await response . json ( ) ;
if ( ! data . within_tolerance ) {
console . log ( ` Out of tolerance by ${ data . difference_pct } % — would go to review ` ) ;
}
Example Response
{
"success" : true ,
"data" : {
"invoice_id" : "7234567890123456789" ,
"system_amount" : 1098.00 ,
"declared_amount" : 1500.00 ,
"within_tolerance" : false ,
"difference" : 402.00 ,
"difference_pct" : 36.61 ,
"difference_abs" : 402.00 ,
"action" : "REVIEW" ,
"config_source" : "proveedor"
} ,
"meta" : { }
}
Errors
Code HTTP Applies to Description VALIDATION_ERROR400 All Invalid params or body (e.g. missing startdate, amount below 0, more than 100 declarations) INVALID_DATE_RANGE400 List Date range exceeds 93 days, or dates are invalid UNAUTHORIZED401 All Missing or invalid JWT token / API key TOKEN_EXPIRED401 All The JWT was valid but has expired (1 hour lifetime) FORBIDDEN403 All The account is not a supplier — "This endpoint requires a supplier account." — or lacks the required permission NOT_FOUND404 Detail, Declare, Validate Invoice does not exist or does not belong to your supplier account RATE_LIMITED429 All Exceeded the endpoint rate limit