POST /v1/invoices
Issues a signed e-CF and submits it to DGII asynchronously.
Request
curl -X POST https://sandbox.api.erply.pro/v1/invoices \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d @invoice.json
Required headers
| Header | Notes |
|---|---|
Authorization | Bearer <jwt> obtained from /v1/auth/token. |
Idempotency-Key | UUID v4. TTL 24 h. |
Content-Type | application/json; charset=utf-8. |
Body
The canonical body lives in tests/fixtures/quickstart_invoice_request.json and is validated against the InvoicePayload model on every commit (see tests/unit/test_quickstart_fixture.py):
{
"type": "31",
"encf": "E310000000001",
"client_request_id": "req-2026-05-01-001",
"company": { "rnc": "131000001", "name": "ERPly Pro Demo S.R.L." },
"customer": { "rnc": "131000002", "name": "Cliente Demo S.R.L." },
"lines": [
{
"line_number": 1,
"description": "Servicio de consultoria",
"quantity": "1",
"unit_price": "5000.00",
"subtotal": "5000.00"
}
],
"totals": { "subtotal": "5000.00", "tax": "900.00", "grand_total": "5900.00" }
}
Contract notes
tenantIddoes not go in the body. The handler derives it from the JWT claims (tenant_id,environment) viaTenantContext.from_event. BUG-EP-03 fixed the drift between the published OpenAPI and the implementation: the field was never accepted by the Lambda body schema.- Decimals as strings.
quantity,unit_price,subtotal,totals.*are accepted as strings to avoid client-side rounding errors; the server validatesΣ lines[].subtotal == totals.subtotal ± 0.01andtotals.subtotal + totals.tax == totals.grand_total. client_request_id(8–64 chars) is the body-level idempotency key; theIdempotency-Keyheader covers network retries. Both coexist — see errors §replay.encfandtypemust agree. The 2nd/3rd characters ofencf(E**...) must equaltype.
Responses
| HTTP | Meaning |
|---|---|
202 Accepted | Accepted, queued for DGII. |
200 OK | Idempotent replay. |
{
"docId": "01HW9X4G…",
"trackId": "20260501-DGII-9988",
"status": "pending",
"_links": {
"self": "/v1/invoices/01HW9X4G…",
"status": "/v1/invoices/01HW9X4G…/status"
}
}
Possible errors
| HTTP | Slug | Cause |
|---|---|---|
| 400 | malformed-json | Body is not JSON. |
| 400 | unsupported-ecf-type | Type outside the catalog. |
| 400 | validation-error | Pydantic failed. |
| 401 | unauthorised | Invalid token. |
| 422 | mathematical-discrepancy | Totals don't match. |
| 422 | tenant-config-missing | Missing RNC/PFX. |
| 422 | dgii-rejected (+ DGII code) | DGII rejection. |
| 504 | dgii-unavailable | DGII timeout. |
See the full DGII error dictionary for the numeric codes.