Nexcar

Cases

A case represents a vehicle. Use this resource to obtain the `case_id` you'll attach documents to.

Authentication: every endpoint on this page requires Authorization: Bearer <token>. None of them accept x-api-key — the /v1/cases series resolves the company and the user from the token. See Authentication.

Endpoints#

MethodPathDescription
GET/v1/casesList cases for the authenticated company (paginated)
POST/v1/casesCreate a case, optionally with initial documents
GET/v1/cases/{case_id}Read the case and its documents (with OCR)
DELETE/v1/cases/{case_id}Soft-delete the case
POST/v1/cases/{case_id}/assignAssign the case to a user in your company
GET/v1/cases/{case_id}/namesNames consolidated from all documents
GET/v1/cases/{case_id}/vinsPrimary VIN and all VINs detected per document
GET/v1/cases/{case_id}/platesLicense plates detected in the case with vigencia data
GET/v1/cases/{case_id}/chain_platesPlate chain (history) for the vehicle
POST/v1/cases/{case_id}/chain_platesGenerate the vehicle's plate chain in the background
GET/v1/cases/{case_id}/ownership_chainOwnership chain (owner history) for the vehicle
POST/v1/cases/{case_id}/ownership_chainGenerate the vehicle's ownership chain in the background
GET/v1/cases/{case_id}/repuveREPUVE registration data for the vehicle
GET/v1/cases/{case_id}/pedimentoMost recent customs entry (pedimento) result
POST/v1/cases/{case_id}/pedimentoTrigger a new customs entry lookup from OCR data
GET/v1/cases/{case_id}/rapiMost recent RAPI (FGJCDMX) criminal activity lookup result
POST/v1/cases/{case_id}/rapiTrigger a new RAPI lookup for the case's primary VIN
GET/v1/cases/{case_id}/renapoMost recent RENAPO CURP validation result
POST/v1/cases/{case_id}/renapoTrigger a RENAPO CURP validation for the case
GET/v1/cases/{case_id}/transunionMost recent TransUnion AutoInfo result for the case
POST/v1/cases/{case_id}/transunionTrigger a TransUnion AutoInfo lookup for the primary VIN
GET/v1/cases/{case_id}/ticketsInfracciones for every plate in the case (read-only)
POST/v1/cases/{case_id}/ticketsTrigger a bulk infracciones lookup for all plates in the case
POST/v1/cases/{case_id}/taxesTrigger a bulk tenencia lookup for all plates in the case
GET/v1/cases/{case_id}/invoicesCase invoices with the result of their four checks
GET/v1/cases/{case_id}/metadataBusiness metadata attached to the case
POST/v1/cases/{case_id}/metadataAppend an entry to the metadata history
POST/v1/cases/{case_id}/processBulk-process the case's documents
GET/v1/cases/{case_id}/statusStatus of the latest processing job

GET /v1/cases — List cases#

Returns a paginated list of cases belonging to the authenticated user's company, ordered from most recent to oldest.

Query parameters#

ParameterTypeDefaultDescription
limitinteger20Page size (max 100)
offsetinteger0Numeric cursor for pagination
internal_idstring—Filter by internal_id (partial match, case-insensitive)
vinstring—Filter by VIN (partial match across case documents)
case_typeUUID—Filter by catalog use case (n_vehicles.case_id, exact match)

curl examples#

# All cases (first page)
curl "https://api.nexcar.mx/v1/cases?limit=20&offset=0" \
  -H "Authorization: Bearer your_jwt_token"

# Filter by internal_id
curl "https://api.nexcar.mx/v1/cases?internal_id=EXP-2026" \
  -H "Authorization: Bearer your_jwt_token"

# Filter by VIN
curl "https://api.nexcar.mx/v1/cases?vin=00000000000000000" \
  -H "Authorization: Bearer your_jwt_token"

# Filter by catalog use case
curl "https://api.nexcar.mx/v1/cases?case_type=a1b2c3d4-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "data": [
    {
      "case_id": "00000000-0000-0000-0000-000000000001",
      "internal_id": "EXP-2026-0001",
      "review_status": "pending",
      "use_case_id": "a1b2c3d4-0000-0000-0000-000000000000",
      "user_id_added_by": "usr-00000000-0000-0000-0000-000000000001",
      "created_by": "usr-00000000-0000-0000-0000-000000000001",
      "created_at": "2026-06-08T10:00:00+00:00",
      "active": true
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 142,
    "has_more": true
  }
}

Field reference#

FieldTypeDescription
case_idUUIDVehicle / case identifier
internal_idstringYour reference identifier
review_statusstring | nullCurrent review status
use_case_idUUID | nullCatalog use case assigned to this case
user_id_added_byUUID | nullUser who created the case
created_byUUID | nullUser attributed as creator
created_atstringISO 8601 creation timestamp
activebooleantrue = active case
pagination.totalintegerTotal matching records
pagination.has_morebooleantrue if there are more pages

Common errors#

HTTPCause
400company_id could not be resolved from the token

POST /v1/cases — Create a case#

Creates a case for a vehicle. You may attach documents in the same call or upload them later.

Body#

FieldTypeRequiredDescription
internal_idstringYesCase identifier on your side. Cannot be empty.
statusstringYesInitial state of the case (e.g. processing, nuevo).
vehicle_originstringNoNacional (default) or Importado.
initial_statusstringNoAlternative initial state when a status catalog is configured.
use_caseUUIDNoUse case for the file when a catalog applies.
metadataobjectNoAdditional business data (see below).
filesarrayNoDocuments to upload immediately.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{
    "internal_id": "EXP-2026-0001",
    "status": "processing",
    "vehicle_origin": "Nacional",
    "metadata": {
      "numero_siniestro": "000000000",
      "tipo_afectado": "TERCERO",
      "nombre_afectado": "NOMBRE APELLIDO APELLIDO"
    },
    "files": [
      {
        "url": "https://your-storage.example.com/invoice.pdf",
        "mime_type": "application/pdf",
        "document_type": "factura"
      },
      {
        "url": "https://your-storage.example.com/owner-id.jpg",
        "mime_type": "image/jpeg",
        "document_type": "ine"
      }
    ]
  }'

Request body#

{
  "internal_id": "EXP-2026-0001",
  "status": "processing",
  "vehicle_origin": "Nacional",
  "metadata": {
    "numero_siniestro": "000000000",
    "tipo_afectado": "TERCERO",
    "nombre_afectado": "NOMBRE APELLIDO APELLIDO"
  },
  "files": [
    {
      "url": "https://your-storage.example.com/invoice.pdf",
      "mime_type": "application/pdf",
      "document_type": "factura"
    },
    {
      "url": "https://your-storage.example.com/owner-id.jpg",
      "mime_type": "image/jpeg",
      "document_type": "ine"
    }
  ]
}

Responses#

201 Created — case created without files:

{ "case_id": "00000000-0000-0000-0000-000000000001", "status": "completed" }

202 Accepted — case created with files queued for processing:

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "job_id": "00000000-0000-0000-0000-000000000003",
  "total_files": 2,
  "status": "processing"
}

Common errors#

HTTPCodeCause
400VALIDATION_ERRORMissing status or empty internal_id
400DUPLICATE_INTERNAL_IDA case with that internal_id already exists with files
422INVALID_STATUSstatus is not part of the configured catalog

GET /v1/cases/{case_id}#

Returns the case and all of its documents along with their OCR (when available).

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555 \
  -H "Authorization: Bearer your_jwt_token"

By default only active documents are returned; use ?files=all to include inactive ones too.

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "review_status": "pending",
  "created_at": "2026-06-08T10:00:00+00:00",
  "files": [
    {
      "file_id": "00000000-0000-0000-0000-000000000002",
      "created_at": "2026-06-08T10:05:00+00:00",
      "document_type": "bank_account",
      "document_subtype": null,
      "mime_type": "application/pdf",
      "active": true,
      "url": "https://...nexcar.mx/storage/v1/object/public/vehicles/.../doc.pdf",
      "ocr": {}
    }
  ]
}

document_subtype is the document's subtype when available, otherwise null.

DELETE /v1/cases/{case_id}#

Marks the case as inactive. Files are not physically deleted; the case stops appearing in lookups and reports.

curl -X DELETE https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555 \
  -H "Authorization: Bearer your_jwt_token"

POST /v1/cases/{case_id}/assign#

Assigns the case to a user in your company — who owns reviewing it. This is the API equivalent of the assignment made from the Nexcar interface, and it writes the same history that view displays.

The assignment history records who assigned the case, and that comes from the token: this endpoint could not accept an API key even if the series allowed it — a key identifies the integration, not the person.

Body#

FieldTypeRequiredDescription
user_idstringYesIdentifier of the user to assign. Both their uid (auth identifier) and their pid (profile identifier) are accepted.

Do not send the history or the date: the server builds them. The date is the server's, in UTC, and the name is resolved from the assigned user's profile.

Example with curl#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/assign \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "usr-00000000-0000-0000-0000-000000000001" }'

Response#

{
  "case_id": "11111111-2222-3333-4444-555555555555",
  "assigned_to": "usr-00000000-0000-0000-0000-000000000001",
  "assignment_history": [
    {
      "date": "2026-08-11T18:42:07.512843+00:00",
      "user": "Ana Ramírez",
      "pid": "pid-00000000-0000-0000-0000-000000000009",
      "label": "Expediente asignado a Ana Ramírez."
    }
  ]
}

Field reference#

FieldTypeDescription
case_idUUIDThe affected case
assigned_tostringThe identifier you sent in user_id, verbatim
assignment_historyarrayFull history, oldest to newest. Every assignment appends an entry; none are replaced.
assignment_history[].datestringServer date in UTC, ISO 8601
assignment_history[].userstringName of the assigned user; their email if they have no name, and un usuario if they have neither
assignment_history[].pidstringpid of the assigned profile
assignment_history[].labelstringDisplay-ready text for an activity log

Membership rules#

Both parties must belong to the company that owns the case:

  • The caller. If your token belongs to another company, the request never reaches the endpoint: it is rejected with 403.
  • The assignee. Validated against the company of the case. Without this rule the case would end up owned by someone who cannot open it, and would vanish from every inbox.

Reassigning is valid and expected: each call appends one more entry to the history, even if you assign the same user who already had it.

Webhook#

If your company has the v1-assign-analyst webhook configured, every assignment triggers it asynchronously. The assignment is saved even if the webhook fails. To enable it, reach out to your Nexcar contact.

Common errors#

HTTPCodeCause
400VALIDATION_ERRORuser_id is missing or empty, or the body is not a JSON object
400VALIDATION_ERRORThe given user does not exist
400VALIDATION_ERRORThe case has no company associated, so the assignment cannot be validated
400VALIDATION_ERRORThe given user's profile has no pid, so it cannot be assigned
403FORBIDDENThe user to assign belongs to another company
403FORBIDDENYour token belongs to a company other than the case's
404RESOURCE_NOT_FOUNDThe case does not exist

POST /v1/cases/{case_id}/process#

Queues the bulk processing (OCR + extraction) of the documents in the case. Useful when you uploaded files without type and want to run the flow as a batch.

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/process \
  -H "Authorization: Bearer your_jwt_token"
{ "job_id": "00000000-0000-0000-0000-000000000003", "status": "processing" }

Track progress with GET /v1/cases/{case_id}/status.

GET /v1/cases/{case_id}/plates#

Returns the license plates detected across the case's documents, enriched with VIN, motor number, owner name and — for plates registered in Estado de México — the current vigencia data (status, validity dates and previous plate, when applicable) from the official portal.

The list is built and persisted automatically as part of the case processing pipeline. This endpoint is read-only.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/plates \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "plates": [
    {
      "plate": "ABC1234",
      "entity": "MEX",
      "document_type": "vehicle_certificate",
      "document_date": "01/09/2025",
      "file_id": "00000000-0000-0000-0000-000000000005",
      "ocr_id": "00000000-0000-0000-0000-000000000006",
      "niv": "00000000000000000",
      "motor_number": "00000000000",
      "owner_name": null,
      "plate_status": "Vigente",
      "plate_valid_from": "01/09/2025",
      "plate_valid_until": "01/09/2030",
      "previous_plate": null,
      "previous_plate_lookup_status": null
    }
  ]
}

Field reference#

FieldTypeDescription
platestringNormalized plate number (uppercase, no separators)
entitystringThree-letter state code (MEX, NLE, JAL, …)
document_typestringSource document: certificate_title, tax_payment, alta_vehicular, vehicle_certificate, vehicle_plate, lumo_checklist or repuve
document_datestringDate associated with the source document (DD/MM/YYYY when available)
nivstring | nullVehicle VIN (17 chars when available, partial otherwise)
motor_numberstring | nullEngine number from the source document
owner_namestring | nullOwner as declared in the source document
plate_statusstring | nullCurrent plate status returned by the portal: Vigente, Vencida or Inactiva
plate_valid_fromstring | nullStart of current plate validity (DD/MM/YYYY)
plate_valid_untilstring | nullEnd of current plate validity (DD/MM/YYYY)
previous_platestring | nullPrevious plate number, when one was emitted before the current one
previous_plate_lookup_statusstring | nullLookup state — see below

previous_plate_lookup_status values#

ValueMeaning
nullThe lookup does not apply (plate not registered in MEX) or the portal confirmed there is no previous plate
"pending"Lookup in progress in background — re-query in a few seconds
"found"Portal returned a previous plate (populated in previous_plate)
"error"Lookup failed after retries (portal down, parsing error, etc.)

The same plate may appear in multiple rows when it was detected in more than one document. All rows sharing the same plate value carry the same plate_status, vigencia dates and previous_plate_* fields once the background lookup finishes.

Common errors#

HTTPCause
404Case not found, inactive, or plates have not been computed yet for this case

GET /v1/cases/{case_id}/chain_plates#

Returns the vehicle's plate chain: the ordered history of plates (Estado de México), each with its validity period and the documents that back it. The chain is computed by the case processing pipeline and persisted; this endpoint is read-only.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/chain_plates \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "niv": "00000000000000000",
  "summary": {
    "plate_count": 2,
    "current_plate": "NEF076A",
    "chain_scope": "edomex_only",
    "generated_at": "2026-08-25T14:22:10Z"
  },
  "chain": [
    {
      "sequence_index": 0,
      "plate": "MEX44J09",
      "entity": "MEX",
      "period_start": "2023-02-14",
      "period_end": "2023-07-04",
      "period_precision": "day",
      "is_current": false,
      "predecessor_plate": null,
      "documents": [
        {
          "file_id": "00000000-0000-0000-0000-000000000005",
          "ocr_id": "00000000-0000-0000-0000-000000000006",
          "source_kind": "ocr",
          "document_type": "vehicle_certificate",
          "field_name": "placa_matricula",
          "plate": "MEX44J09",
          "folio": "0150337",
          "issued_at": "2023-02-14",
          "date_source": "document_field",
          "valid_until": null,
          "owner_name": null,
          "niv": "00000000000000000",
          "niv_matches_case_niv": true,
          "file_url": null
        }
      ]
    }
  ],
  "status": "completed",
  "message": null
}

Status#

statusMeaning
completedThe chain is generated — returned in summary + chain
processingThe chain is being generated in the background — retry in a few seconds
pendingThe case has no chain yet — trigger the POST

For processing/pending, summary is null and chain is []; all other fields are always present.

Field reference#

FieldTypeDescription
nivstring | nullVehicle VIN (from the case's consolidated VIN)
summary.plate_countnumberNumber of plates in the chain
summary.current_platestring | nullCurrent plate
summary.chain_scopestringChain scope (edomex_only)
chain[].sequence_indexnumberChronological order (0 = oldest)
chain[].platestringPlate for this segment
chain[].period_start / period_endstring | nullSegment validity (YYYY-MM-DD)
chain[].is_currentbooleanWhether this is the current plate
chain[].predecessor_platestring | nullImmediately preceding plate
chain[].documentsarrayDocuments backing the segment

Common errors#

HTTPCause
404Case not found or inactive

POST /v1/cases/{case_id}/chain_plates#

Triggers generation of the vehicle's plate chain in the background. It is fire-and-forget: it responds immediately with status: "processing" and the chain becomes available via the GET once it finishes. It takes no parameters (only the path case_id).

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/chain_plates \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "niv": "00000000000000000",
  "summary": null,
  "chain": [],
  "status": "processing",
  "message": "La generación de la cadena de placas se está ejecutando en background."
}

Common errors#

HTTPCause
400case_id is not a valid UUID, or parameters were sent in the body
404Case not found or inactive

GET /v1/cases/{case_id}/ownership_chain#

Returns the vehicle's ownership chain: the ordered history of owners with their holding periods, the documents backing each, and the gaps where a transfer could not be traced. The chain is computed by the case processing pipeline and persisted; this endpoint is read-only.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/ownership_chain \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "summary": {
    "owner_count": 2,
    "gap_count": 1,
    "current_owner": "JUAN PEREZ",
    "chain_pivot": "invoice_original",
    "generated_at": "2026-08-27T14:22:10Z"
  },
  "chain": [
    {
      "sequence_index": 0,
      "display_name": "FÁBRICA SA",
      "person_type": "moral",
      "is_current": false,
      "acquired_at": "2023-02-14",
      "transferred_at": "2023-07-04",
      "match_status": "matched",
      "link_to_previous": null,
      "source_document": { "document_type": "invoice", "folio": "…", "file_id": "…" }
    }
  ],
  "gaps": [
    {
      "gap_id": "g1",
      "gap_type": "untraceable_transfer",
      "related_sequence_indexes": [0, 1]
    }
  ],
  "status": "completed",
  "message": null
}

Status#

statusMeaning
completedThe chain is generated — summary + chain + gaps (chain: [] with completed is a legitimate empty result).
processingBeing generated in the background (the chain, or the names consolidation it depends on). Retry in a few seconds.
pendingThe case has no chain yet — trigger the POST.

For processing/pending, summary is null and chain/gaps are []; all other fields are always present. Do not assume a fixed field set inside chain[]/gaps[] — the worker owns that shape.

Common errors#

HTTPCause
404Case not found or inactive

POST /v1/cases/{case_id}/ownership_chain#

Triggers generation of the ownership chain in the background. It is fire-and-forget: it responds immediately with status: "processing" and the chain becomes available via the GET once it finishes. It takes no parameters (only the path case_id). If the case has no consolidated names yet, generation runs that consolidation first (a slower path) — size your polling accordingly.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/ownership_chain \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "internal_id": "EXP-2026-0001",
  "summary": null,
  "chain": [],
  "gaps": [],
  "status": "processing",
  "message": "La generación de la cadena de propiedad se está ejecutando en background."
}

Common errors#

HTTPCause
400case_id is not a valid UUID, or parameters were sent in the body
404Case not found or inactive

GET /v1/cases/{case_id}/repuve#

Returns the most recent REPUVE job for the case. If the job ended in error but a valid response from placas.info is available, the endpoint auto-recovers and returns the correct data.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/repuve \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "codigo": "ok",
  "info": {
    "niv": "00000000000000000",
    "placa": "ABC1234",
    "marca": "NISSAN",
    "modelo": "NP300/NP300 FRONTIER/FRONTIER",
    "tipo": "CAB. Y CHASIS ESTACAS",
    "clase": "CAMIONETA",
    "anio_modelo": 2020,
    "entidad_emplacado": "CIUDAD DE MEXICO",
    "fecha_inscripcion": "22/09/20",
    "fecha_actualizacion": "12/10/24",
    "reporte_robo": [
      { "tipo_reporte": "FGJ", "estatus": "SIN REPORTE DE ROBO" },
      { "tipo_reporte": "OCRA", "estatus": "SIN REPORTE DE ROBO" }
    ],
    "robo_usa_can": { "tiene_robo": false },
    "aviso_judicial": { "tiene_aviso_judicial": false }
  },
  "job_id": "00000000-0000-0000-0000-000000000004",
  "job_status": "completed"
}

Field reference#

FieldTypeDescription
codigostringok — REPUVE data available. error — all sources failed. processing — job still in progress.
infoobject | nullVehicle registration data from REPUVE. null when the vehicle is not registered or the job has not completed yet.
info.nivstringVehicle identification number (VIN)
info.placastring | nullLicense plate on record in REPUVE
info.marcastringMake
info.modelostringModel
info.anio_modelointegerModel year
info.entidad_emplacadostring | nullState where the plate was registered
info.fecha_inscripcionstring | nullREPUVE registration date
info.fecha_actualizacionstring | nullLast update date in REPUVE
info.reporte_roboarrayTheft reports from FGJ and OCRA sources
info.robo_usa_canobjectUSA/Canada theft report status
info.aviso_judicialobjectJudicial notice status
job_idUUIDInternal job identifier
job_statusstringcompleted, error, processing or pending

Common errors#

HTTPCause
404Case not found, inactive, or no REPUVE query has been run for this case

GET /v1/cases/{case_id}/pedimento#

Returns the most recent customs entry (pedimento) result for the case. Data is read from pedimento_responses (legacy pipeline) and processing_jobs (v1 API), deduplicated by content, and sorted by recency.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/pedimento \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "job_id": "00000000-0000-0000-0000-000000000004",
  "status": "completed",
  "source": "processing_jobs",
  "result": {
    "success": true,
    "data": { "pedimento": "00000000000", "aduana": "MANZANILLO", "vin": "00000000000000000" }
  }
}

Common errors#

HTTPCause
404Case not found, inactive, or no pedimento query has been run for this case. Call POST /v1/cases/{id}/pedimento first.

GET /v1/cases/{case_id}/taxes#

Returns the case's tenencia information as two independent blocks, each with a {message, status, result} envelope. It is read-only: it never triggers new lookups, and it always responds 200 when the case exists — missing inputs are reported with result: [] and a message.

Each block's status has only two values: completed (the block reached its outcome: there is enough to build it — even if the content is a business error — or the case has no plate to look up at all) and processing (a lookup is still pending: a plate of the case whose query is in flight or has never run).

Contract change (August 21, 2026). The two no-plates empties — "No se encontraron placas registradas" and "Las placas registradas no corresponden al VIN del expediente" — move from processing to completed. No lookup is in flight, so polling returned the same envelope forever; the message is what explains the empty result. If your integration retries on processing, read the message instead to see what is missing. A case that has plates but no lookup yet ("Aún no se ha ejecutado ninguna consulta de tenencia para este caso.") stays processing — that one is resolved by a POST.

The plates published by this endpoint are the ones that belong to the case, not everything accumulated in n_vehicles.parsed_data.plates: entries whose niv matches the case VIN are kept (parsed_data.vin.value — exact match, or a partial niv that is a suffix of the VIN) and entries carrying a different VIN are dropped, since they belong to another vehicle. An empty niv does not disqualify (some documents carry a plate but no VIN). The identity is the (plate, entity) pair: the same plate registered in two states means two distinct lookups and both are published. Among several entries of the same pair, the most recent document_date wins. If the case has no consolidated VIN yet, nothing is filtered.

main_tax block#

The case's main plate is the extracted plate with the most recent document_date; plates without a date do not compete. If none has a date, the first legible plate wins. The block comes back completed with result: [] only when no plate in the case is legible.

result is a fixed grid of 5 fiscal years (current year + 4 back, descending). Each row crosses two sources for the same year:

FieldMeaning
fiscal_yearThe fiscal year of the window
plate_numberThe main plate
stateThe plate's entity (abbreviated)
documentA {status, message} cell: whether a tenencia receipt (tax_payment) for that plate declares that fiscal year
sourceA {status, message} cell: what the state's portal says about that fiscal year
payment_dateFrom the receipt, paired to the fiscal year; null when there is no receipt or the pairing failed
totalThe portal's when it reports the fiscal year with an amount; otherwise the receipt's

document cell:

statusmessageWhen
success"Agregado"A tenencia receipt covers that fiscal year
warning"Pendiente"No receipt covers that fiscal year

source cell: the portal reports outstanding debt, not payments — a fiscal year showing up there means the portal found a pending debt for that year, not that it was paid.

statusmessageWhen
warning"Con adeudo"The portal reports that fiscal year
success"Sin adeudo"The portal answered, but without that fiscal year
failedThe portal's message verbatimUseful reason (e.g. "Vehículo no encontrado o dado de baja")
failed"Hubo un error al obtener la información"Source error with no publishable detail
failed"No se pudo ejecutar la consulta de tenencia para esta placa."The lookup never ran
processing"Se está procesando la información"The lookup is still running
processing"Aún no se ha consultado la tenencia de esta placa"The plate was never looked up

Messages stay in Spanish — they are display text for end users. When the lookup returned fiscal years, each year is resolved on its own; when it returned none, all 5 years share the same verdict.

taxes block#

The case's plates (main + previous) except the main plate, which main_tax already publishes in full. Each plate is fed by the same two sources as main_tax — receipt and portal — but without a fixed grid (one item per fiscal year that either source reported) and with a single cell per item.

FieldMeaning
plate_numberThe plate
stateThe plate's entity (abbreviated); the receipt's when the plate has none
fiscal_yearThe fiscal year, or null when neither source reported anything for that plate
statusA {status, message} cell: what the portal says about that fiscal year
payment_dateFrom the receipt for that plate, paired to the fiscal year
totalThe portal's when it reports the fiscal year with an amount; otherwise the receipt's

status cell — the same cell as source in main_tax, unmodified:

statusmessageWhen
success"Sin adeudo"The portal reports no debt for that fiscal year
warning"Con adeudo"The portal reports debt for that fiscal year
failedThe portal's own reason, or a sanitized generic messageA real error: it could not be determined whether there is debt
processing"Se está procesando la información" / "Aún no se ha consultado la tenencia de esta placa"The lookup is in flight, or the plate has not been queried yet

This block carries no document cell: the per-year receipt breakdown lives only in main_tax. The plate's receipt still contributes the fiscal year, the payment_date and the fallback total.

A receipt counts for the plate it declares, not for the case: a tenencia receipt uploaded to the case that turns out to belong to another plate shows up here, under its own plate, and does not vouch for the main plate.

A fiscal year can come from a single source: the portal reports debt for 2026 with no receipt on file (payment_date at null), or there is a receipt for a year the portal never reported (status at success / "Sin adeudo", with its payment_date).

When a plate has several lookups, the most informative one wins, not the most recent: a failed retry does not hide an earlier attempt that did return fiscal years.

If the main plate is the only plate in the case, the block returns completed with result: [] and the message "El caso no tiene placas adicionales a la principal" — it is permanently empty, so there is nothing to poll for. Same when the case has no plates, or none of them belongs to the case VIN: completed, with the message telling which of the two empties it is. If the main plate could not be determined, nothing is excluded. If the case has no tenencia lookup at all, the block stays processing but still publishes whatever receipts exist.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/taxes \
  -H "Authorization: Bearer your_jwt_token"

Response#

{
  "main_tax": {
    "message": "",
    "status": "completed",
    "result": [
      {
        "fiscal_year": 2026,
        "plate_number": "ABC1234",
        "state": "MEX",
        "document": { "status": "success", "message": "Agregado" },
        "source": { "status": "warning", "message": "Con adeudo" },
        "payment_date": "16/03/2026",
        "total": 1779
      },
      {
        "fiscal_year": 2025,
        "plate_number": "ABC1234",
        "state": "MEX",
        "document": { "status": "warning", "message": "Pendiente" },
        "source": { "status": "warning", "message": "Con adeudo" },
        "payment_date": null,
        "total": 2071
      },
      {
        "fiscal_year": 2024,
        "plate_number": "ABC1234",
        "state": "MEX",
        "document": { "status": "success", "message": "Agregado" },
        "source": { "status": "success", "message": "Sin adeudo" },
        "payment_date": "13/02/2024",
        "total": "1,045.00"
      },
      {
        "fiscal_year": 2023,
        "plate_number": "ABC1234",
        "state": "MEX",
        "document": { "status": "warning", "message": "Pendiente" },
        "source": { "status": "success", "message": "Sin adeudo" },
        "payment_date": null,
        "total": null
      },
      {
        "fiscal_year": 2022,
        "plate_number": "ABC1234",
        "state": "MEX",
        "document": { "status": "warning", "message": "Pendiente" },
        "source": { "status": "success", "message": "Sin adeudo" },
        "payment_date": null,
        "total": null
      }
    ]
  },
  "taxes": {
    "message": "",
    "status": "completed",
    "result": [
      { "plate_number": "XYZ5678", "state": "MEX", "fiscal_year": 2026,
        "status": { "status": "warning", "message": "Con adeudo" }, "payment_date": "16/03/2026", "total": 1739.0 },
      { "plate_number": "XYZ5678", "state": "MEX", "fiscal_year": 2025,
        "status": { "status": "warning", "message": "Con adeudo" }, "payment_date": null, "total": 2031.0 },
      { "plate_number": "JKL9012", "state": "PUE", "fiscal_year": null,
        "status": { "status": "failed", "message": "Vehículo no encontrado o dado de baja" }, "payment_date": null, "total": null }
    ]
  }
}

When the case has no extracted plates yet:

{
  "main_tax": { "message": "No se encontraron placas registradas", "status": "completed", "result": [] },
  "taxes":    { "message": "No se encontraron placas registradas", "status": "completed", "result": [] }
}

POST /v1/cases/{case_id}/taxes#

Triggers a bulk tenencia lookup for all unique plates in the case (main plates + previous plates), using GET /v1/cases/:id/plates data internally (no HTTP). One processing_jobs child row is created per plate, each with its own job_id, external_payload and external_response. The workflow runs in Temporal (invoice-background-queue).

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/taxes \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{
  "job_id": "00000000-0000-0000-0000-000000000003",
  "plates": ["ABC1234", "XYZ5678"],
  "status": "pending"
}

Poll GET /v1/cases/{case_id}/taxes to see the results per plate as they complete. Before the first job finishes, that GET's taxes block comes back with status: "processing" and result: [].

Common errors#

HTTPCause
400No plates found for this case. Call POST /v1/cases/{id}/plates first.
404Case not found or inactive

POST /v1/cases/{case_id}/pedimento#

Triggers a new customs entry lookup using data extracted from the OCR of the case's primary document (the invoice linked to the main VIN). Runs asynchronously in Temporal. Returns a job_id immediately; poll GET /v1/cases/{id}/pedimento for the result.

The required parameters (aduana, ano_vehiculo) are read from the OCR automatically. If they are not present in the document, the endpoint returns a 400 listing the missing fields.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/pedimento \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{ "job_id": "00000000-0000-0000-0000-000000000004", "status": "pending" }

Common errors#

HTTPCause
400No primary VIN found, or required OCR fields (aduana, ano_vehiculo) are missing
404Case not found or inactive

GET /v1/cases/{case_id}/rapi#

Returns the most recent RAPI (FGJCDMX criminal activity registry) lookup result for the case. Data is read from rapi_responses (legacy pipeline) and processing_jobs (v1 API), deduplicated by content, and sorted by recency.

curl example#

curl https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/rapi \
  -H "Authorization: Bearer your_jwt_token"

Response#

Every response returns the same set of fields: job_id, result, status, and message (message is null when not applicable).

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "job_id": "00000000-0000-0000-0000-000000000004",
  "status": "completed",
  "message": null
}

When there is no RAPI result yet but the case is being processed, the response is 200 with the case status (instead of 404):

{ "job_id": null, "result": null, "status": "processing", "message": "El caso está en procesamiento; la información de RAPI puede completarse en background." }
{ "job_id": null, "result": null, "status": "pending", "message": "El caso aún no está siendo procesado." }

Possible status: completed, error, processing (case in progress, RAPI may complete in the background), and pending (case not processed yet).

Common errors#

HTTPCause
404Case not found or inactive, or no case job and no RAPI query exist. Call POST /v1/cases/{id}/rapi first.

POST /v1/cases/{case_id}/rapi#

Triggers a RAPI lookup for the case's primary VIN (from GET /v1/cases/:id/vins). Runs asynchronously in Temporal (invoice-background-queue). Returns a job_id immediately.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/11111111-2222-3333-4444-555555555555/rapi \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{ "job_id": "00000000-0000-0000-0000-000000000004", "vin": "00000000000000000", "status": "pending" }

Common errors#

HTTPCause
400No primary VIN found. Process documents first with POST /v1/cases/{id}/process.
404Case not found or inactive

GET /v1/cases/{case_id}/renapo#

Returns the most recent RENAPO CURP validation result for the case. Returns 404 if no query has been run yet.

curl example#

curl https://api.nexcar.mx/v1/cases/{case_id}/renapo \
  -H "Authorization: Bearer your_jwt_token"

Response 200 OK#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "source": "renapo",
  "status": "completed",
  "query_count": 1,
  "completed_count": 1,
  "data": {
    "job_id": "00000000-0000-0000-0000-000000000002",
    "created_at": "2026-06-01T10:00:00+00:00",
    "status": "completed",
    "curp": "XXXX800101HDFXXX01",
    "probative_doc_url": "https://...",
    "person": {
      "first_name": "FIRST_NAME",
      "paternal_surname": "SURNAME1",
      "maternal_surname": "SURNAME2",
      "birth_date": "01/01/1980",
      "birth_state": "CIUDAD DE MEXICO",
      "birth_country": "MEXICO",
      "sex": "HOMBRE",
      "curp_status": "AN",
      "validation_status": "OK",
      "validation_code": "00",
      "message_code": "0",
      "probative_doc": null
    },
    "probative_doc_data": {
      "registration_year": "1980",
      "state_key": "09",
      "municipality_key": "010",
      "state": "CIUDAD DE MEXICO",
      "municipality": "ALVARO OBREGON",
      "foja": null,
      "folio": "123",
      "book": null,
      "tome": null,
      "act_number": "456",
      "foreign_registry_number": null
    }
  }
}

When status is pending or error, the person and probative_doc_data fields inside data are null.

Field reference#

FieldTypeDescription
case_idUUIDCase identifier
sourcestringAlways "renapo"
statusstringcompleted, pending, or error
query_countintegerTotal queries run for this case
completed_countintegerNumber of completed queries
data.job_idUUIDQuery job identifier
data.curpstringCURP that was validated
data.probative_doc_urlstring | nullURL of the probative document when available
data.personobject | nullValidated person data (populated when status = completed)
data.probative_doc_dataobject | nullCivil registry data from the probative document (populated when available)

Common errors#

HTTPCause
404Case not found, inactive, or no RENAPO query has been run. Call POST /v1/cases/{case_id}/renapo first.

POST /v1/cases/{case_id}/renapo#

Triggers a RENAPO CURP validation for the case. The CURP is automatically selected from the case's processed OCR documents (priority: curp document type > id_front / id_front_and_reverse > others). The operation is asynchronous: returns a job_id immediately and the result is available via GET /v1/cases/{case_id}/renapo once completed.

Requires that the case has at least one document with processed OCR containing a CURP value.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/{case_id}/renapo \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{
  "job_id": "00000000-0000-0000-0000-000000000002",
  "curp": "XXXX800101HDFXXX01",
  "status": "pending"
}

Poll GET /v1/cases/{case_id}/renapo for the result once completed.

Common errors#

HTTPCause
400No CURP available in the case. Process documents with POST /v1/cases/{case_id}/process first.
404Case not found or inactive

GET /v1/cases/{case_id}/transunion#

Returns the most recent TransUnion result for the case. Returns 404 if no query has been run yet.

curl example#

curl https://api.nexcar.mx/v1/cases/{case_id}/transunion \
  -H "Authorization: Bearer your_jwt_token"

Response 200 OK#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "source": "transunion",
  "status": "completed",
  "query_count": 1,
  "completed_count": 1,
  "data": { },
  "document": {
    "url": "https://...nexcar.mx/storage/v1/object/public/vehicles/.../transunion_report.pdf",
    "uuid": "00000000-0000-0000-0000-000000000003"
  }
}

data holds the TransUnion report (present when status is completed; null otherwise). document references the generated AutoVerificación report PDF. When status is processing or error, data is null.

Common errors#

HTTPCause
404Case not found, inactive, or no TransUnion query has been run. Call POST /v1/cases/{case_id}/transunion first.

POST /v1/cases/{case_id}/transunion#

Triggers a TransUnion lookup for the case's primary VIN and generates the AutoVerificación report PDF from the result. The operation is asynchronous: returns a job_id immediately and the result is available via GET /v1/cases/{case_id}/transunion once completed.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/{case_id}/transunion \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{ "job_id": "00000000-0000-0000-0000-000000000002", "vin": "00000000000000000", "status": "pending" }

Common errors#

HTTPCause
400No primary VIN found. Process documents first with POST /v1/cases/{case_id}/process.
404Case not found or inactive

GET /v1/cases/{case_id}/tickets#

Returns the infractions for the plates that belong to the case: the main plate first, then the rest (previous plates included).

The plates published by this endpoint are the ones that belong to the case, not everything accumulated in n_vehicles.parsed_data.plates: entries whose niv matches the case VIN are kept (parsed_data.vin.value — exact match, or a partial niv that is a suffix of the VIN) and entries carrying a different VIN are dropped, since they belong to another vehicle. An empty niv does not disqualify (some documents carry a plate but no VIN). The identity is the (plate, entity) pair: the same plate registered in two states means two distinct lookups and both are published. Among several entries of the same pair, the most recent document_date wins. If the case has no consolidated VIN yet, nothing is filtered.

The main plate is picked among those plates, so a document belonging to another vehicle can never lead the result.

This endpoint is read-only: it never triggers new queries. Use POST /v1/cases/{case_id}/tickets for that.

Response 200 OK#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "status": "completed",
  "message": null,
  "result": [
    {
      "plate_number": "ABC123",
      "state": "PUE",
      "ticket_date": "2024-01-23",
      "amount": 1037.4,
      "status": "success",
      "message": "Pagada"
    },
    {
      "plate_number": "XYZ456",
      "state": "MEX",
      "ticket_date": null,
      "amount": null,
      "status": "warning",
      "message": "Placa sin adeudos"
    }
  ]
}

Every row in result carries the same six keys, so there is no need to check for their existence:

FieldDescription
plate_numberPlate the row belongs to
stateEntity the plate was queried in (null when unknown)
ticket_dateDate of the infraction
amountAmount exactly as the source reports it
status"success", "warning", "processing", or "failed" (see table below)
messageHuman-readable text for the row: the payment status when it's an infraction, or the reason when the plate contributes no infractions

status is "success" when the infraction is paid, or when the plate was queried and has no debts; "processing" while the query is still running; "warning" only for an infraction that was found and is unpaid; every other case — including a source-provided business reason or a plate never queried — is "failed":

Casestatusmessage
Infraction with PAGADA status at the sourcesuccessPagada
Infraction with NO PAGADA status at the sourcewarningNo Pagada
Infraction with any other status (PENDIENTE, …)warningThe text exactly as the source reports it
Plate with no infractionssuccessPlaca sin adeudos
Query still runningprocessingSe está procesando la información
Query could not run (process or provider failure)failedHubo un problema al extraer la información
Plate never queriedfailedAún no se ha consultado esta placa
Source answered with its own reason — e.g. Entidad aún sin coberturafailed(source text, not a closed set)

Each infraction is one row. A plate that contributes no infractions takes up a single row with ticket_date and amount set to null and its reason in message.

A plate is queried in its own licensing entity and in CMX (Mexico City infractions apply to any plate). When both queries have a record, the plate appears twice in result — once per state — each with its own rows; neither entity replaces the other.

The difference between "could not run" and a source-provided reason: Hubo un problema al extraer la información means the query never ran (the provider did not answer, throttled the request, or authentication failed) and retrying is worthwhile. A source-provided reason means the query did reach the portal, which explained why there is no data; retrying will not change it.

Root status values#

ValueMeaning
completedThe read reached its outcome: information was found for at least one plate (message is null), or the case has no plate to look up at all (message says which empty it is)
processingA lookup is still pending: the case has plates whose query is in flight or has never run; message says why

completed does not mean "everything went well": it only takes one record for any plate — with infractions, with no debts, failed, or still running. Each plate's detail is read from its row message.

When result comes back empty, message is one of:

messagestatusMeaning
No se encontraron placas registradascompletedThe case has no plates. Call POST /v1/cases/{case_id}/plates
Las placas registradas no corresponden al VIN del expedientecompletedThe case has plates, but none belongs to the case VIN (e.g. the uploaded receipt is from another vehicle). Review the case documents or the consolidated VIN
Aún no se ha ejecutado ninguna consulta de infracciones para este casoprocessingThere are plates but they were never queried. Call POST /v1/cases/{case_id}/tickets

Contract change (August 21, 2026). The two no-plates empties move from processing to completed: with no plates there is no lookup in flight, and polling returned the same envelope forever. status says whether the read reached its outcome, not whether there is data — the reason for the empty result lives in message.

Common errors#

HTTPCause
400case_id is not a valid UUID
404Case not found or inactive

Missing plates or missing queries are not errors: the endpoint answers 200 with result: [] and the message that explains it — completed when there is no plate to look up, processing when there are plates and the lookup is still missing.

POST /v1/cases/{case_id}/tickets#

Triggers an infraction lookup for all plates in the case (current plates and previous plates). The operation is asynchronous: returns a job_id immediately and results become available via GET /v1/cases/{case_id}/tickets as they complete.

Requires that the case has plates already computed. If not, call POST /v1/cases/{case_id}/plates first.

curl example#

curl -X POST https://api.nexcar.mx/v1/cases/{case_id}/tickets \
  -H "Authorization: Bearer your_jwt_token"

Response 202 Accepted#

{
  "job_id": "00000000-0000-0000-0000-000000000002",
  "plates": ["ABC123", "XYZ456"],
  "status": "pending"
}

Poll GET /v1/cases/{case_id}/tickets to see the results per plate as they complete.

Common errors#

HTTPCause
400No plates found for this case. Call POST /v1/cases/{case_id}/plates first.
404Case not found or inactive

GET /v1/cases/{case_id}/invoices#

Returns one entry per invoice document (document_type = invoice) in the case, both active and inactive, ordered by created_at descending. Each entry carries the fields OCR extracted from the document plus the result of four checks:

KeyWhat it checks
validate_invoiceThat the CFDI is registered with the SAT
consulta_pedimentoThe vehicle's customs entry (pedimento) with the SAT
amda_invoiceWhether the document is an AMDA invoice and what vehicle data it carries
amisThe vehicle's insurance policy history (AMIS)

All four keys are always present (never null) and always have the same shape:

{ "status": "processing" | "completed", "message": string|null, "result": object|null, "updated_at": string|null }

message and result are mutually exclusive: when result is present (the check found information), message is null; when there is no result, message explains why.

The checks run in the background while the case is processed, so this endpoint may return partial results. Not paginated; returns invoices: [] when the case has no invoice documents.

status values#

statusMeaning
processingThe check has not run yet for that invoice. message is "Consulta en procesamiento" and updated_at is null
completedA response is available. This covers three outcomes: found (result is present), not found (message) and the lookup failed (message with the reason)

status describes the information found, not the internal job execution: a job that ended in error is published as completed with the reason in message. "Not found" and "failed" are told apart by the text of message, not by status.

curl example#

curl https://api.nexcar.mx/v1/cases/{case_id}/invoices \
  -H "Authorization: Bearer $TOKEN"

Response 200 OK#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "invoices": [
    {
      "consulta_pedimento": {
        "status": "completed",
        "message": "El vehículo es de procedencia nacional",
        "result": null,
        "updated_at": "2026-07-30T22:22:52.573318"
      },
      "amis": {
        "status": "completed",
        "message": null,
        "result": {
          "info": [
            {
              "marca": "VOLKSWAGEN",
              "submarca": "JETTA",
              "anio_modelo": 2012,
              "poliza": "******123",
              "cancelable": "SI",
              "inicio_vigencia": "2026-04-28",
              "fin_vigencia": "2027-04-28"
            }
          ]
        },
        "updated_at": "2026-07-30T22:23:23.227505"
      },
      "amda_invoice": {
        "status": "processing",
        "message": "Consulta en procesamiento",
        "result": null,
        "updated_at": null
      },
      "validate_invoice": {
        "status": "completed",
        "message": null,
        "result": {
          "claveMensaje": "0",
          "codigoValidacion": "fd1700000000.1234567",
          "efectoComprobante": "ingreso",
          "estadoCFDI": "Vigente",
          "estatusCancelacion": "Cancelable con aceptación",
          "fechaCertificacion": "2013-03-26T16:05:02",
          "fechaEmision": "2013-03-26T15:58:18",
          "folioFiscal": "3F1A2B3C-0000-0000-0000-000000000000",
          "nombreEmisor": "DISTRIBUIDORA DEMO, S.A. DE C.V.",
          "nombreReceptor": "JUAN PEREZ LOPEZ",
          "rfcEmisor": "AAA010101AAA",
          "rfcPac": "PAC0807172W8",
          "rfcReceptor": "BBB020202BBB",
          "totalCfdi": "$113,400.00"
        },
        "updated_at": "2026-07-30T22:22:52.602585+00:00"
      },
      "data": {
        "folio_fiscal": { "db_field": "fiscal_number", "label": "Folio fiscal", "text": "3F1A2B3C-0000-0000-0000-000000000000" },
        "rfc_emisor": { "db_field": "issuer_rfc", "label": "RFC emisor", "text": "AAA010101AAA" },
        "rfc_receptor": { "db_field": "receiver_rfc", "label": "RFC receptor", "text": "BBB020202BBB" }
      },
      "updated_at": "2026-07-30T22:22:17.453961+00:00",
      "file_id": "00000000-0000-0000-0000-000000000001",
      "file_url": "https://…/storage/v1/object/public/vehicles/…/factura.pdf"
    }
  ]
}

Response 200 OK — checks not run yet#

A freshly uploaded invoice, or one whose processing has not run, returns all four sources as processing:

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "invoices": [
    {
      "consulta_pedimento": { "status": "processing", "message": "Consulta en procesamiento", "result": null, "updated_at": null },
      "amis":               { "status": "processing", "message": "Consulta en procesamiento", "result": null, "updated_at": null },
      "amda_invoice":       { "status": "processing", "message": "Consulta en procesamiento", "result": null, "updated_at": null },
      "validate_invoice":   { "status": "processing", "message": "Consulta en procesamiento", "result": null, "updated_at": null },
      "data": null,
      "updated_at": null,
      "file_id": "00000000-0000-0000-0000-000000000002",
      "file_url": "https://…/factura_2.pdf"
    }
  ]
}

Response 200 OK — case with no invoices#

{
  "case_id": "00000000-0000-0000-0000-000000000001",
  "invoices": []
}

Field reference#

FieldTypeDescription
case_idUUIDCase / vehicle identifier
invoicesarrayOne entry per invoice document found in the case
invoices[].file_idUUIDInvoice file identifier
invoices[].file_urlstring | nullPublic URL of the invoice file
invoices[].dataobject | nullSanitized OCR fields extracted from the invoice (null if OCR is not available). Each field carries db_field, label and text
invoices[].updated_atstring | nullLast update of that invoice's OCR (ISO 8601)
invoices[].validate_invoiceobjectCFDI check with the SAT
invoices[].consulta_pedimentoobjectCustoms entry (pedimento) lookup
invoices[].amda_invoiceobjectAMDA lookup
invoices[].amisobjectAMIS insurance history. It belongs to the vehicle, so it repeats identically across every invoice in the case

Fields of each check object:

FieldTypeDescription
statusstringprocessing or completed (see the table above)
messagestring | nullResult text when there is no result. Mutually exclusive with result
resultobject | nullInformation found. Only keys that carry data are published; empty ones are omitted
updated_atstring | nullTimestamp of the published result. null while status is processing

Contents per check#

Checkresult when foundmessage when not
validate_invoiceSAT response: estadoCFDI (Vigente / Cancelado), folioFiscal, rfcEmisor, nombreEmisor, rfcReceptor, nombreReceptor, totalCfdi, fechaEmision, estatusCancelacion"Este comprobante no se encuentra registrado en los controles del Servicio de Administracion Tributaria…"
consulta_pedimentoSAT tables that carry data: vehiculo_importado, situacion_pedimentos, importacion_vehiculos"El vehículo es de procedencia nacional" (the invoice has no import data — this is a valid result) · "No se encontró información" · "Servicio no disponible." (the SAT site did not respond)
amda_invoiceamda_code and vehicle_details (vin, brand, model, dealer_name, …)"No se encontró información" · the provider's reason, e.g. "No se encontraron los indicadores de una Factura AMDA…" · "Hubo un error al procesar la información"
amisPolicy list under info or data depending on which process ran the lookup: read both keys"Sin datos" · "No se encontró información" · the provider's reason, e.g. "Longitud incorrecta (se esperan 17 caracteres)" when the case VIN is invalid

Messages are returned in Spanish regardless of the documentation language.

validate_invoice and consulta_pedimento are resolved per invoice: the first is matched by fiscal data (CFDI folio_fiscal, then the issuer/receiver RFC pair) and the second by the file that originated the lookup and by the pedimento number from OCR. In a case with several invoices, each one carries its own result.

The response includes both active and inactive invoices and does not flag which one is current.

Common errors#

HTTPCause
400id is not a valid UUID
401Missing Authorization: Bearer <token>, or the token is invalid or expired
404Case not found

INE Document Validation#

These endpoints validate an official Mexican identification card (INE/IFE) by performing OCR on the front and back images. The operation is asynchronous: the POST returns a job_id immediately and the result becomes available via GET once processing completes.

MethodPathDescription
POST/documents/ineSubmit an INE document for validation
GET/documents/ine/{job_id}Retrieve the validation result

POST /documents/ine#

Submits an INE document for OCR extraction. The process runs in the background; use GET /documents/ine/{job_id} to retrieve the result.

Body#

Each field accepts a public URL or the file in base64 (with or without the data: prefix).

FieldTypeRequiredDescription
frontstringYesFront side of the ID: URL or base64
backstringNoBack side of the ID: URL or base64 (improves extraction accuracy)

curl example (URL)#

curl -X POST https://api.nexcar.mx/documents/ine \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{
    "front": "https://your-storage.example.com/id-front.jpg",
    "back": "https://your-storage.example.com/id-back.jpg"
  }'

curl example (base64)#

curl -X POST https://api.nexcar.mx/documents/ine \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{
    "front": "data:image/jpeg;base64,<...>",
    "back": "data:image/jpeg;base64,<...>"
  }'

Response 202 Accepted#

{
  "job_id": "00000000-0000-0000-0000-000000000001",
  "status": "processing"
}

Poll GET /documents/ine/{job_id} to retrieve the result once it is ready.

Common errors#

HTTPCodeCause
400MISSING_PARAMETERfront is missing or empty
401—Invalid or missing API key

GET /documents/ine/{job_id}#

Returns the result of a previously submitted INE validation.

curl example#

curl https://api.nexcar.mx/documents/ine/00000000-0000-0000-0000-000000000001 \
  -H "Authorization: Bearer your_jwt_token"

Response 200 OK — completed#

{
  "status": "completed",
  "data": {
    "names": "FIRST NAME",
    "first_last_name": "SURNAME1",
    "second_last_name": "SURNAME2",
    "birth_date": "01/01/1990",
    "gender": "HOMBRE",
    "curp": "XXXX900101HDFXXX01",
    "elector_key": "XXXXXX000000000000",
    "state": "CIUDAD DE MEXICO",
    "municipality": "ALCALDIA EJEMPLO",
    "section": "1234",
    "emission": "01",
    "validity": "2030",
    "cic": "000000000",
    "ocr": "000000000000",
    "citizen_identifier": "000000000000000000000000000"
  }
}

Response 200 OK — still processing#

{ "status": "processing", "data": null }

Response 200 OK — failed#

{ "status": "error", "data": null }

status values#

ValueMeaning
processingValidation in progress — retry in a few seconds
completedValidation finished successfully; data contains extracted fields
errorValidation failed; data is null

Field reference (data)#

FieldTypeDescription
namesstringFirst name(s)
first_last_namestringFirst surname
second_last_namestringSecond surname
birth_datestringDate of birth (DD/MM/YYYY)
genderstringGender (HOMBRE / MUJER)
curpstringCURP
elector_keystringVoter registry key
statestringState of registration
municipalitystringMunicipality of registration
localitystring | nullLocality of registration
sectionstringElectoral section
emissionstringEmission number
validitystringYear through which the card is valid
cicstring | nullInternal credential identifier
ocrstring | nullOCR number from the card
citizen_identifierstring | nullCitizen identifier
typestring | nullCard type (e.g. C, D)
issue_datestring | nullIssue date (DD/MM/YYYY)
expiry_datestring | nullExpiry date (DD/MM/YYYY)
streetstring | nullStreet address on the card
neighborhoodstring | nullNeighborhood
citystring | nullCity

All fields in data are present only when readable from the document. Fields not detected on the card are omitted from the response.

Common errors#

HTTPCodeCause
404NOT_FOUNDjob_id not found
401—Invalid or missing API key

INE Credential Validity Check#

Validates a voter ID credential (INE/IFE) from its cic and citizen_identifier and confirms whether it is valid as a means of identification. The operation is asynchronous: the POST returns a job_id immediately and the result becomes available via GET once the validation finishes.

MethodPathDescription
POST/validate/ineSubmit an INE credential for validation
GET/validate/ine/{job_id}Read the validation result

POST /validate/ine#

Starts the validation of an INE credential. Returns a job_id to poll for the result.

Body#

All fields are optional, but you must send at least one. Which fields are required depends on the credential type:

  • Type C: ocr + elector_key + emission_number
  • Type D: cic + ocr
  • Types E/F/G/H: cic + citizen_identifier
FieldTypeDescription
cicstringCIC printed on the credential
citizen_identifierstringCitizen identifier of the credential
elector_keystringVoter key
ocrstringOCR number of the credential
emission_numberstringIssue number of the credential

curl example#

curl -X POST https://api.nexcar.mx/validate/ine \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cic": "000000000",
    "citizen_identifier": "000000000"
  }'

Response 202 Accepted#

{
  "job_id": "00000000-0000-0000-0000-000000000001",
  "status": "processing"
}

Poll the result with GET /validate/ine/{job_id}.

GET /validate/ine/{job_id}#

Returns the status and result of an INE validation.

curl example#

curl https://api.nexcar.mx/validate/ine/00000000-0000-0000-0000-000000000001 \
  -H "Authorization: Bearer YOUR_TOKEN"

Response 200 OK#

{
  "status": "completed",
  "data": {
    "status": "OK",
    "message": "Esta vigente como medio de identificacion y puede votar",
    "validity": "31 de diciembre de 2027",
    "emission_year": "2017",
    "registration_year": "1996",
    "emission_number": "2",
    "cic": "000000000",
    "elector_key": "XXXXXX00000000X000",
    "message_code": "3",
    "ocr": "0000000000000",
    "federal_district": "00",
    "local_district": "00"
  }
}

When status is processing or error, data is null.

status values#

ValueMeaning
processingValidation still in progress. Poll again in a few seconds.
completedValidation finished. data holds the result.
errorValidation could not be completed.

Common errors#

HTTPCause
400No field provided (at least one is required)
404Job not found