Status codes & errors
Every non-2xx response shares the same envelope. This page lists every stable error.code, the four validation levels behind VALIDATION_FAILED, and the record flags LTFRB reviewers act on.
Two kinds of code appear on this page:top-level error.code— always present, one of the stable codes in the table near the bottom — anderror.details[].code— zero or more validation-finding codes nested inside it (the L1-L4 codes below are this kind).
Validation levels (L1-L4)
The transmission pipeline runs all four levels and collects every finding before deciding — read the whole error.details[] array, not just the first entry.
Required
Every mandatory field is present: externalTransactionId, issuingInsurerCode, policyNumber, issuanceDate, coverageType, cocNumber, effectiveDate, expirationDate, operator.name, operator.operatorType, operator.region, at least one vehicle, and — for every vehicle — plateNumber, mvFileNumber, engineNumber, chassisNumber, vehicleType, denomination, passengerCapacity, region. Any failure here rejects the whole transmission.
REQUIREDFormat
Field-level shape checks from the active validation rule set: ISO dates, expirationDate > effectiveDate, coverage span ≤ maxCoverageDays (default 400), issuanceDate ≤ today+1, plate/MV-file/chassis/engine patterns after normalization, passengerCapacity 1-100, coverageType must be one of the active rule set's coverageTypes (default: PPAI_STANDARD), operator.contactEmail must be a valid address if given, vehicle count ≤ maxVehiclesPerCertificate, yearModel not in the future, and no duplicate identifiers within the same payload.
INVALID_FORMATINVALID_DATE_RANGECOVERAGE_TOO_LONGUNSUPPORTED_COVERAGE_TYPEDUPLICATE_IDENTIFIER_IN_PAYLOADInsurer / transmit authority
The transmitting organization is ACTIVE; the issuing insurer exists, is an ACTIVE INSURANCE_COMPANY; the transmitting org has authority to transmit for it (is the issuing insurer itself, or the lead insurer / assigned management company of a pool the issuing insurer belonged to on the issuance date); pool membership existed on that date if a pool code was given; and, for the machine API, the API client is ACTIVE with the required scope.
UNKNOWN_INSURERINSURER_NOT_ACTIVEORGANIZATION_NOT_ACTIVENOT_AUTHORIZED_TO_TRANSMITPOOL_MEMBERSHIP_NOT_FOUNDMISSING_SCOPEVehicle reference match
Each vehicle's four identifiers are looked up against LTFRB/LTO reference data. All four matching one reference vehicle is a full match; a partial match (warning only) does not block registration; identifiers spanning two different reference vehicles, or a contradicting hit, are reported for review. No reference data available for a region degrades to a review warning rather than a hard failure.
REFERENCE_MATCHEDREFERENCE_PARTIAL_MATCHREFERENCE_FOR_REVIEWREFERENCE_NO_MATCHREFERENCE_DATA_UNAVAILABLEVerified against the validation engine
apps/api/src/modules/validation/level1.ts through level4.ts. L1 and L3 findings are always severity: "ERROR"; L2 findings are also always ERROR; L4 findings are INFO for REFERENCE_MATCHED and WARNING for everything else — L4 never blocks registration on its own. Still branch on level and field as a fallback for any code not yet listed here.Policy-level amendment rule: POLICY_SHARED_BY_OTHER_CERTIFICATES
Live (apps/api/src/modules/transmission/amend.ts:101-176): POST …/amendments rejects with HTTP 422 when the amendment changes a policy-level field (changes.policy.issuanceDate, coverageType, premiumAmount, officialReceiptNumber, remarks, or changes.poolCode) while other certificates share the same policy — those fields apply to every certificate issued under that policy, so they can't be changed from just one certificate's amendment. Cancel and re-transmit, or coordinate the change across every affected certificate instead.
Exact shape of the response
- Top-level
error.codeisVALIDATION_FAILED— the same code any other failed amendment returns. - One entry per changed field in
error.details[], each withcode:POLICY_SHARED_BY_OTHER_CERTIFICATES, e.g.field: "changes.policy.premiumAmount"orfield: "changes.poolCode".
So: check error.code === "VALIDATION_FAILED" first, then look for this specific code inside error.details[] to tell it apart from any other validation failure. See Amendments in the integration guide.
Record flags
A flag does not block registration — the record is still written, with a visible warning until an LTFRB reviewer clears it or refers it back to the insurer.
2026-09-17 amendment: overlap is now rejected, not flagged
VEHICLE_ALREADY_INSURED (422) instead of being registered with a flag. As a result, POSSIBLE_DUPLICATE and OVERLAPPING_COVERAGE are no longer raised by new transmissions — the enum values are retained only so existing (pre-amendment) flagged records keep their history. IDENTIFIER_CONFLICT and VEHICLE_MISMATCH are unaffected and remain active flags. See Idempotency & duplicates in the integration guide.| Flag type | Status | When it's raised |
|---|---|---|
| POSSIBLE_DUPLICATE | Historical only | Historical rows only — before the 2026-09-17 amendment, raised for a different policy number with identical effective/expiration dates on the same canonical vehicle. |
| OVERLAPPING_COVERAGE | Historical only | Historical rows only — before the 2026-09-17 amendment, raised for overlapping coverage dates on the same canonical vehicle. Superseded by the VEHICLE_ALREADY_INSURED rejection. |
| VEHICLE_MISMATCH | Active | L4 reference match came back NO_MATCH or FOR_REVIEW for one or more vehicles (configurable). |
| IDENTIFIER_CONFLICT | Active | A vehicle's identifiers matched two or more different canonical vehicles; the most specific identifier wins and the conflict is flagged. |
Flag statuses: OPEN, CLEARED, REFERRED_TO_INSURER, RESOLVED_BY_AMENDMENT.
Error codes
| Code | HTTP | Meaning | Remediation |
|---|---|---|---|
UNAUTHENTICATED | 401 | No valid session cookie or bearer token was presented. | Sign in again to LTFRB Admin, or exchange fresh OAuth2 client credentials (machine API). |
FORBIDDEN | 403 | The authenticated actor does not hold the permission or scope this action requires. | Confirm the user's role or the API client's scopes; ask an admin to grant the missing one. |
MFA_REQUIRED | 401 | Multi-factor authentication has not been completed, is not yet enrolled, or the login challenge expired. | Complete `POST /auth/v1/mfa/enroll` then `POST /auth/v1/mfa/verify`, or restart `POST /auth/v1/login`. |
INVALID_CREDENTIALS | 401 | Email/password (or current password on a change request) did not match. | Re-check the credentials. This message is intentionally generic and does not reveal which field was wrong. |
ACCOUNT_LOCKED | 401 | 5 consecutive failed sign-in attempts locked the account for 15 minutes. | Wait for the lock to expire, or have an LTFRB admin unlock the user. |
SESSION_EXPIRED | 401 | The session cookie is missing, invalid, or past its idle (15 min) or absolute (8 h) timeout. | Sign in again — the LTFRB Admin SPA should redirect to `/login` on any 401 from `/admin/v1/*`. |
CSRF_FAILED | 403 | A mutating session request was missing the `X-PPAI-CSRF: 1` header or its `Origin` was not in `CORS_ORIGINS`. | Send `X-PPAI-CSRF: 1` on every non-GET request to `/admin/v1/*`, from an allowed origin. |
INVALID_TOKEN | 401 | The `Authorization: Bearer` access token is missing, malformed, expired, or unknown. | Request a new token from `POST /api/v1/oauth/token` — tokens expire after 15 minutes (`expires_in: 900`). |
INVALID_SIGNATURE | 401 | `X-PPAI-Signature` did not match the HMAC-SHA256 the server computed for this exact request. | Recompute the signature from the exact bytes sent (method, path+query, timestamp, nonce, body). See /docs/integration. |
TIMESTAMP_OUT_OF_RANGE | 401 | `X-PPAI-Timestamp` (unix seconds) is more than 300 seconds away from server time. | Sync the client clock (NTP) and generate the timestamp immediately before sending the request. |
NONCE_REUSED | 401 | `X-PPAI-Nonce` was already used by this client within the last 10 minutes. | Generate a fresh random nonce (16-64 chars) per request; never cache or replay a signed request. |
IP_NOT_ALLOWED | 403 | The API client has a non-empty IP allowlist and the caller's IP is not on it. | API clients are issued and managed by LTFRB, not self-service — contact LTFRB to have the calling IP/CIDR added to the client's allowlist (managed in the admin portal). |
RATE_LIMITED | 429 | The per-client (default 120/min), per-IP token (20/min) or per-IP login (10/min) rate limit was exceeded. | Back off and retry after the `Retry-After` header value; if your integration consistently needs more throughput, ask LTFRB about raising your client's per-minute limit. |
VALIDATION_FAILED | 422 | The request body failed schema parsing or the transmission pipeline's L1-L4 validation. | Read `error.details[]` — each entry has `field`, `code`, `level` and a human-readable `message`. |
DUPLICATE_TRANSMISSION | 422 | The same policy + COC for this issuing insurer is already registered with an identical covered-vehicle set. | This is expected on a retried submit with a new `externalTransactionId`; no action needed — check `error.details.ltfrbReference`. |
COC_ALREADY_REGISTERED | 422 | The policy + COC is already registered for this issuing insurer but the vehicle set differs. | Use `POST …/amendments` against the existing `ltfrbReference` instead of re-transmitting. |
IDEMPOTENCY_KEY_REUSED | 409 | `externalTransactionId` was already used by this org with a different payload (hash mismatch). | Idempotency keys must be unique per distinct payload. Generate a new one for genuinely new submissions. |
TRANSMISSION_IN_PROGRESS | 409 | A transmission with the same `externalTransactionId` is still `RECEIVED`/`VALIDATING`. | Retry after a short delay; do not change the payload or the idempotency key while retrying. |
IMMUTABLE_FIELD | 422 | An amendment tried to change `issuingInsurerCode`, `policy.policyNumber` or `certificate.cocNumber`. | Cancel the record and transmit a new one instead of amending an identity field. |
RECORD_MISMATCH | 422 | The `policyNumber`/`cocNumber` on a cancellation request does not match the target record. | Fetch the record first (`GET /ppai/policies/{ltfrbReference}`) and copy its exact identifiers. |
RECORD_CANCELLED | 422 | An amendment or cancellation was attempted on a record whose lifecycle is already `CANCELLED`. | Cancelled records are terminal; transmit a new certificate if coverage needs to resume. |
NOT_FOUND | 404 | The referenced record, transmission, vehicle or resource does not exist (or is outside your visibility scope). | Double-check the reference/identifier and that your organization has visibility on the record. |
CONFLICT | 409 | Generic conflict — e.g. MFA already enrolled, or a concurrent update changed the resource. | Re-fetch the current state and retry with up-to-date data. |
PAYLOAD_TOO_LARGE | 413 | The request body exceeded the size limit. For the machine API (POST /api/v1/ppai/policies and friends) that's 1 MB of JSON — comfortably above what a certificate with the maximum 500 vehicles needs. LTFRB's own reference-data imports (POST /admin/v1/reference-data/imports, not an insurer endpoint) allow up to 10 MB. | Insurers integrate via the JSON machine API only — there is no bulk file upload. Split unusually large certificates by transmitting vehicles you can't fit as a follow-up amendment. |
UNSUPPORTED_FILE | 415 | An uploaded file's extension, magic bytes, size or structure didn't match an accepted type. This is an LTFRB-internal condition — POST /admin/v1/reference-data/imports (LTFRB reference-data imports) is the only endpoint that can return it. Insurers integrate via JSON only and never upload files, so this code should never appear in a response to an insurer API client. | Not applicable to insurer integrations. (LTFRB staff: re-export the reference-data file from the template and re-upload.) |
INVALID_MFA_CODE | 401 | The 6-digit TOTP code was incorrect, expired, or already used. | Enter the current code from your authenticator app; codes rotate every 30 seconds. |
VEHICLE_ALREADY_INSURED | 422 | A vehicle in the payload already has in-force PPAI coverage — across ALL insurers, not just yours. A vehicle may only have one in-force coverage at a time (spec §3.4, 2026-09-17 amendment); this is checked by identifier (plate, MV file, engine or chassis — including historical identifiers, any match counts), applies to new transmissions and to amendments that change dates or vehicles. Cancelled certificates never block, and a renewal starting after the existing expiration is allowed. `error.details[]` has one entry per affected vehicle, e.g. `{ field: "vehicles[0].plateNumber", code: "VEHICLE_ALREADY_INSURED", message: "Vehicle ABC 1234 is already insured by PAMI until 2027-03-01." }` — no competitor policy/COC numbers or operator data are disclosed. | Confirm with the operator whether existing coverage is real; if so, wait for it to expire (or have it cancelled) before transmitting, or correct the vehicle identifiers if this is a false match. |
INTERNAL_ERROR | 500 | An unexpected server error. The response never includes a stack trace. | Note the `requestId` and contact LTFRB support if the problem persists. |