Skip to content
PPAI-ETMSDeveloper Portal

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.

L1

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.

REQUIRED
L2

Format

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_PAYLOAD
L3

Insurer / 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_SCOPE
L4

Vehicle 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_UNAVAILABLE

Verified against the validation engine

Every code listed above is taken directly from 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.code is VALIDATION_FAILED — the same code any other failed amendment returns.
  • One entry per changed field in error.details[], each with code: POLICY_SHARED_BY_OTHER_CERTIFICATES, e.g. field: "changes.policy.premiumAmount" or field: "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

A vehicle may have only one in-force PPAI coverage at a time, across all insurers (spec §3.4). A new transmission or amendment that would overlap an existing in-force certificate on the same vehicle — by any identifier match, from any insurer — is now rejected outright with 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 typeStatusWhen it's raised
POSSIBLE_DUPLICATEHistorical onlyHistorical 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_COVERAGEHistorical onlyHistorical 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_MISMATCHActiveL4 reference match came back NO_MATCH or FOR_REVIEW for one or more vehicles (configurable).
IDENTIFIER_CONFLICTActiveA 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

CodeHTTPMeaningRemediation
UNAUTHENTICATED401No valid session cookie or bearer token was presented.Sign in again to LTFRB Admin, or exchange fresh OAuth2 client credentials (machine API).
FORBIDDEN403The 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_REQUIRED401Multi-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_CREDENTIALS401Email/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_LOCKED4015 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_EXPIRED401The 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_FAILED403A 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_TOKEN401The `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_SIGNATURE401`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_RANGE401`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_REUSED401`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_ALLOWED403The 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_LIMITED429The 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_FAILED422The 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_TRANSMISSION422The 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_REGISTERED422The 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_REUSED409`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_PROGRESS409A 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_FIELD422An 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_MISMATCH422The `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_CANCELLED422An 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_FOUND404The 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.
CONFLICT409Generic 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_LARGE413The 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_FILE415An 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_CODE401The 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_INSURED422A 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_ERROR500An unexpected server error. The response never includes a stack trace.Note the `requestId` and contact LTFRB support if the problem persists.