{"openapi":"3.0.0","paths":{"/batches":{"post":{"description":"Use this to send a CSV file when your system produces files. The file has one header line and one certificate per line, with the same fields as the rows of a batch sent as JSON: `policy_number`, `holder_name`, `holder_address`, `registration`, `chassis_number`, `engine_number`, `vehicle_type`, `valid_from` and `valid_to` are required. Columns can be separated by commas, semicolons or tabs, in any order, in UTF-8 or Windows-1252. A file holds up to 20,000 rows. **Download the insurer file template** gives you the header line and an example row to start from.\n\nSend the file as `multipart/form-data` in the field `file`, as a plain `text/csv` body, or as JSON `{ \"csv\": \"…\" }`. The call returns as soon as the file is received; the rows are processed in the background. Follow the batch with **Get a batch with its rows**, or on the Integrations page.\n\nUploading the same file twice returns the first batch with `alreadyReceived: true` and issues nothing a second time. A file that is missing a required column is refused as a whole, and the answer names the columns.\n\n**Access.** Send an integration key or a signed-in session. The caller needs the permission `batch.submit`; choose it when you create the key under Integrations → Integration keys.","operationId":"uploadBatchFile","parameters":[{"name":"kind","in":"query","required":false,"description":"`CSV` for an insurer’s file (the default). National bureaux send `NATIONAL_FEED`.","schema":{"default":"CSV","type":"string","enum":["CSV","NATIONAL_FEED"]},"example":"CSV"},{"name":"sourceSystem","in":"query","required":false,"description":"National bureaux only: the layout of the national file, for example `NIC_MID`.","schema":{"type":"string","maxLength":30}},{"name":"fileName","in":"query","required":false,"description":"A label for the batch. Defaults to the name of the uploaded file.","schema":{"type":"string","maxLength":200},"example":"tema-renewals-october.csv"}],"responses":{"201":{"description":"The file was received and its rows are being processed.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileReceipt"},"examples":{"received":{"summary":"Received","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"CSV","sourceSystem":"INSURER_CSV","fileName":"tema-renewals-october.csv","status":"RECEIVED","total":2,"issued":0,"held":0,"rejected":0,"duplicate":0,"pending":2,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":null},"alreadyReceived":false}}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `FILE_MISSING`: The call carried no file.\n- `UNRECOGNISED_LAYOUT`: The file lacks a required column; `errors` names each one.\n- `CSV_MALFORMED`: The file is not valid CSV.\n- `FILE_EMPTY`: The file has a header but no rows.\n- `TOO_MANY_ROWS`: The file has more than 20,000 rows.\n- `VALIDATION`: A field is missing or has a value the API does not accept; `errors` lists each one.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FILE_MISSING":{"summary":"400 FILE_MISSING","description":"The call carried no file.","value":{"type":"urn:bc:problem:file_missing","title":"Bad request","status":400,"code":"FILE_MISSING","detail":"Choose the file to upload.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"UNRECOGNISED_LAYOUT":{"summary":"400 UNRECOGNISED_LAYOUT","description":"The file lacks a required column; `errors` names each one.","value":{"type":"urn:bc:problem:unrecognised_layout","title":"Bad request","status":400,"code":"UNRECOGNISED_LAYOUT","detail":"The file has no column for: engineNumber. Check that it is in the Insurer file layout.","errors":[{"path":"engineNumber","message":"Column not found"}],"correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"CSV_MALFORMED":{"summary":"400 CSV_MALFORMED","description":"The file is not valid CSV.","value":{"type":"urn:bc:problem:csv_malformed","title":"Bad request","status":400,"code":"CSV_MALFORMED","detail":"The file could not be read as CSV (problem near line 12). Check the quotes and separators.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"FILE_EMPTY":{"summary":"400 FILE_EMPTY","description":"The file has a header but no rows.","value":{"type":"urn:bc:problem:file_empty","title":"Bad request","status":400,"code":"FILE_EMPTY","detail":"The file has no rows below the header line.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"TOO_MANY_ROWS":{"summary":"400 TOO_MANY_ROWS","description":"The file has more than 20,000 rows.","value":{"type":"urn:bc:problem:too_many_rows","title":"Bad request","status":400,"code":"TOO_MANY_ROWS","detail":"A file holds up to 20,000 rows. Split it and upload the parts.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"VALIDATION":{"summary":"400 VALIDATION","description":"A field is missing or has a value the API does not accept; `errors` lists each one.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","errors":[{"path":"csv","message":"Invalid input: expected string, received undefined"}],"correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: The key is missing, mistyped or revoked, or the session has expired.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"The key is missing, mistyped or revoked, or the session has expired.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"403":{"description":"Forbidden. The `code` in the body says which of these it is.\n\n- `FORBIDDEN`: The key does not carry `batch.submit`, or its organisation is not an insurer.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FORBIDDEN":{"summary":"403 FORBIDDEN","description":"The key does not carry `batch.submit`, or its organisation is not an insurer.","value":{"type":"urn:bc:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Bulk files are submitted by an insurer.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests for this key. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"Upload a CSV file","tags":["Upload a file"],"x-integrator":true,"requestBody":{"required":true,"description":"The file, as `multipart/form-data` (field `file`), `text/csv`, or this JSON form.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUpload"},"examples":{"csv":{"summary":"Two certificates","value":{"csv":"policy_number,holder_name,holder_address,registration,chassis_number,engine_number,vehicle_type,valid_from,valid_to\nPOL-GH-7782001,Naa Dedei Ashong,\"Teshie, Accra\",BA 5567-26,KMHCT41DAEU612904,G4FG-2204772,CAR,2026-10-18,2027-10-17\nPOL-GH-7782002,Adwoa Serwaa,\"Madina, Accra\",GT 3342-25,JTNBE40K703118427,1NZ-3492018,CAR,2026-10-19,2027-10-18","fileName":"tema-renewals-october.csv"}}}}}},"security":[{"apiKey":[]},{"bearer":[]}]},"get":{"description":"Use this to see the batches your organisation has sent, newest first, whether by API call or by file. Filter by `status` to find batches that are still running, or by `kind` to separate API calls from uploaded files.\n\nThe list is paged. Ask for `limit` batches (1 to 200, default 25) starting at `offset`, and read `total` to know how many match. Repeat with `offset` raised by `limit` until `items` comes back shorter than `limit`.\n\n**Access.** Send an integration key or a signed-in session. The caller needs the permission `batch.read`; choose it when you create the key under Integrations → Integration keys.","operationId":"listBatches","parameters":[{"name":"kind","in":"query","required":false,"description":"`API` for batches sent as JSON, `CSV` for uploaded files, `NATIONAL_FEED` for a bureau’s national file.","schema":{"type":"string","enum":["CSV","API","NATIONAL_FEED"]},"example":"API"},{"name":"status","in":"query","required":false,"description":"Only batches in this state.","schema":{"type":"string","enum":["RECEIVED","PROCESSING","COMPLETED","FAILED"]},"example":"PROCESSING"},{"name":"limit","in":"query","required":false,"description":"How many batches to return, from 1 to 200.","schema":{"default":25,"type":"integer","minimum":1,"maximum":200},"example":25},{"name":"offset","in":"query","required":false,"description":"How many batches to skip.","schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"example":0}],"responses":{"200":{"description":"A page of batches.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchPage"},"examples":{"page":{"summary":"Two batches","value":{"items":[{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"COMPLETED","total":4,"issued":2,"held":1,"rejected":1,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":"2026-10-12T08:14:04.907Z"},{"id":"b83d6f10-92c4-4e57-8a1d-5f0c7e2a9b64","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"CSV","sourceSystem":"INSURER_CSV","fileName":"tema-renewals-october.csv","status":"COMPLETED","total":120,"issued":118,"held":2,"rejected":0,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-11T15:40:22.118Z","completedAt":"2026-10-11T15:40:31.640Z"}],"total":2,"limit":25,"offset":0}}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `VALIDATION`: A field is missing or has a value the API does not accept; `errors` lists each one.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"VALIDATION":{"summary":"400 VALIDATION","description":"A field is missing or has a value the API does not accept; `errors` lists each one.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: The key is missing, mistyped or revoked, or the session has expired.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"The key is missing, mistyped or revoked, or the session has expired.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"403":{"description":"Forbidden. The `code` in the body says which of these it is.\n\n- `FORBIDDEN`: The key was created without the permission this operation needs.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FORBIDDEN":{"summary":"403 FORBIDDEN","description":"The key was created without the permission this operation needs.","value":{"type":"urn:bc:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"You do not have permission to do this.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests for this key. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"List your batches","tags":["Follow your batches"],"x-integrator":true,"x-permissions":["batch.read"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/batches/{id}":{"get":{"description":"Use this to read a batch and the rows in it, with the values that were read from each row. It is the view to use when a row was held or rejected: the row lists its issues, and a held or rejected row also carries `fields`, the values as read, so you can see what to correct.\n\nRows are listed in the order you sent them. Filter with `result` to see only the rows that need attention, for example `HELD`. The row list is paged with `limit` (1 to 500, default 100) and `offset`.\n\n**Access.** Send an integration key or a signed-in session. The caller needs the permission `batch.read`; choose it when you create the key under Integrations → Integration keys.","operationId":"getBatch","parameters":[{"name":"id","in":"path","required":true,"description":"The id of the batch, from the `batch.id` of the answer to sending it.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648"},{"name":"result","in":"query","required":false,"description":"Only rows that ended this way.","schema":{"type":"string","enum":["PENDING","ISSUED","HELD","REJECTED","DUPLICATE"]},"example":"HELD"},{"name":"limit","in":"query","required":false,"description":"How many rows to return, from 1 to 500.","schema":{"default":100,"type":"integer","minimum":1,"maximum":500},"example":100},{"name":"offset","in":"query","required":false,"description":"How many rows to skip.","schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"example":0}],"responses":{"200":{"description":"The batch and a page of its rows.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchDetail"},"examples":{"rows":{"summary":"An issued row and a held row","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"COMPLETED","total":4,"issued":2,"held":1,"rejected":1,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":"2026-10-12T08:14:04.907Z"},"rows":{"items":[{"rowNo":1,"result":"ISSUED","policyNumber":"POL-GH-7781204","registration":"GR 4821-24","holderName":"Kojo Essien","validFrom":"2026-10-15","validTo":"2027-10-14","certificateId":"7c1f0a52-3a8e-4b8d-9f6e-2d4a9c1b7e30","certificateNumber":"GH-2026-0004711","issues":[],"correctedAt":null},{"rowNo":3,"result":"HELD","policyNumber":"POL-GH-7781205","registration":"GT 3342-25","holderName":"Ama Boateng","validFrom":"2026-10-15","validTo":"2027-10-14","certificateId":null,"certificateNumber":null,"issues":[{"level":"error","code":"MISSING_FIELD","field":"holderAddress","message":"Missing: policyholder's address."}],"correctedAt":null,"fields":{"policyNumber":"POL-GH-7781205","holderName":"Ama Boateng","registration":"GT 3342-25","chassisNumber":"MHFXW42G603061877","engineNumber":"2KD-5503816","vehicleType":"PICKUP","validFrom":"2026-10-15","validTo":"2027-10-14"}}],"total":2,"limit":100,"offset":0}}}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `VALIDATION`: A field is missing or has a value the API does not accept; `errors` lists each one.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"VALIDATION":{"summary":"400 VALIDATION","description":"A field is missing or has a value the API does not accept; `errors` lists each one.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: The key is missing, mistyped or revoked, or the session has expired.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"The key is missing, mistyped or revoked, or the session has expired.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"403":{"description":"Forbidden. The `code` in the body says which of these it is.\n\n- `FORBIDDEN`: The key was created without the permission this operation needs.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FORBIDDEN":{"summary":"403 FORBIDDEN","description":"The key was created without the permission this operation needs.","value":{"type":"urn:bc:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"You do not have permission to do this.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"404":{"description":"Not found. The `code` in the body says which of these it is.\n\n- `NOT_FOUND`: No batch with this id belongs to your organisation.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"NOT_FOUND":{"summary":"404 NOT_FOUND","description":"No batch with this id belongs to your organisation.","value":{"type":"urn:bc:problem:not_found","title":"Not found","status":404,"code":"NOT_FOUND","detail":"The batch was not found.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests for this key. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"Get a batch with its rows","tags":["Follow your batches"],"x-integrator":true,"x-permissions":["batch.read"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/batches/{id}/rows/{rowNo}/correct":{"post":{"operationId":"BatchesController_correct","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"rowNo","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":""}},"tags":["batches"],"x-permissions":["feed.correct"]}},"/batches/{id}/retry":{"post":{"operationId":"BatchesController_retry","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["batches"],"x-permissions":["feed.correct"]}},"/api/batches":{"post":{"description":"Use this to issue many certificates from your policy system in one call. Send up to 20,000 rows; each row is one policy, one vehicle and one period of insurance.\n\nEvery row is checked against the content a certificate must carry under Convention Art. 15(4): the insurer, the policyholder's name and address, the policy number, the first and last day of the period of insurance, the registration number, the vehicle type, the engine and chassis numbers, and the countries covered. A row that has everything and passes the format checks is **issued**. A row missing an item is **held**, so it can be completed and issued later. A row with an invalid value, such as an impossible date, is **rejected** with the reason. A row for a policy, vehicle and start date that already has a certificate is reported as a **duplicate**, and nothing is issued twice.\n\nA batch of up to 500 rows is answered at once with `200` and a result for every row. A larger batch is accepted with `202`; poll `pollUrl` until `batch.status` is `COMPLETED`, then read the results from the same address.\n\n**Dates and periods.** Dates are calendar days in UTC, written `YYYY-MM-DD` or `DD/MM/YYYY`. A certificate runs for two years at most. A cover note (`kind: \"COVER_NOTE\"`) is valid for one month at most. A period that has already ended is rejected.\n\n**Vehicles.** A vehicle that another insurer already covers on any day of the period is held with `VEHICLE_ALREADY_COVERED`; the issue names that insurer and the day its cover ends. A row for a vehicle you already cover replaces your earlier certificate from the new start date, which then reads `REPLACED`.\n\n**Credits.** Each issued certificate uses one certificate credit. When your credits run out, the remaining rows are held with `STOCK_EMPTY` and are issued once credits are added and the batch is retried from the Integrations page.\n\n**Retrying safely.** Send an `Idempotency-Key` with every batch. Sending the same rows again, after a timeout for example, returns the first batch with `alreadyReceived: true` and issues nothing a second time. Reusing a key for different rows is refused with `IDEMPOTENCY_KEY_REUSED`.\n\nBureaux sending a national file name the layout in `sourceSystem` and the insurer of each row in `insurerCode`.\n\n**Access.** Send an integration key or a signed-in session. The caller needs the permission `batch.submit`; choose it when you create the key under Integrations → Integration keys.","operationId":"submitBatch","parameters":[{"name":"sourceSystem","in":"query","required":false,"description":"National bureaux only: the national system the rows come from, for example `NIC_MID`. Insurers leave it out.","schema":{"type":"string","maxLength":30}},{"name":"Idempotency-Key","in":"header","required":true,"description":"A value you choose and keep for this request, up to 200 characters. Send the same value when you retry and you get the original result back instead of a second one. Use a new value for each new request, for example the policy reference with the cover start date.","schema":{"type":"string","minLength":1,"maxLength":200},"example":"policy-GH-7781204-2026-10-12"}],"responses":{"200":{"description":"The batch finished within the call. `results` has one entry for every row, in the order you sent them.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchSubmission"},"examples":{"mixed":{"summary":"Two issued, one held, one rejected","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"COMPLETED","total":4,"issued":2,"held":1,"rejected":1,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":"2026-10-12T08:14:04.907Z"},"results":[{"rowNo":1,"result":"ISSUED","certificateNumber":"GH-2026-0004711","issues":[]},{"rowNo":2,"result":"ISSUED","certificateNumber":"GH-2026-0004712","issues":[]},{"rowNo":3,"result":"HELD","certificateNumber":null,"issues":[{"level":"error","code":"MISSING_FIELD","field":"holderAddress","message":"Missing: policyholder's address."}]},{"rowNo":4,"result":"REJECTED","certificateNumber":null,"issues":[{"level":"error","code":"INVALID_DATE","field":"validTo","message":"\"31/02/2027\" is not a date. Use day/month/year or YYYY-MM-DD."}]}],"alreadyReceived":false}},"replay":{"summary":"The same rows sent again","description":"The first batch is returned and nothing is issued a second time.","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"COMPLETED","total":4,"issued":2,"held":1,"rejected":1,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":"2026-10-12T08:14:04.907Z"},"results":[{"rowNo":1,"result":"ISSUED","certificateNumber":"GH-2026-0004711","issues":[]},{"rowNo":2,"result":"ISSUED","certificateNumber":"GH-2026-0004712","issues":[]},{"rowNo":3,"result":"HELD","certificateNumber":null,"issues":[{"level":"error","code":"MISSING_FIELD","field":"holderAddress","message":"Missing: policyholder's address."}]},{"rowNo":4,"result":"REJECTED","certificateNumber":null,"issues":[{"level":"error","code":"INVALID_DATE","field":"validTo","message":"\"31/02/2027\" is not a date. Use day/month/year or YYYY-MM-DD."}]}],"alreadyReceived":true}}}}}},"202":{"description":"The batch was accepted and continues in the background. Poll `pollUrl` (relative to the API address, after `/api/integration`) until `batch.status` is `COMPLETED`.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchSubmission"},"examples":{"accepted":{"summary":"A batch of 2,000 rows","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"PROCESSING","total":2000,"issued":0,"held":0,"rejected":0,"duplicate":0,"pending":2000,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":null},"pollUrl":"/api/integration/api/batches/4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","alreadyReceived":false}}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `IDEMPOTENCY_KEY_REQUIRED`: The `Idempotency-Key` header is missing or longer than 200 characters.\n- `VALIDATION`: `rows` is missing, empty, or holds more than 20,000 entries; `errors` names the field.\n- `SOURCE_REQUIRED`: A national bureau sent a batch without `sourceSystem`.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"IDEMPOTENCY_KEY_REQUIRED":{"summary":"400 IDEMPOTENCY_KEY_REQUIRED","description":"The `Idempotency-Key` header is missing or longer than 200 characters.","value":{"type":"urn:bc:problem:idempotency_key_required","title":"Bad request","status":400,"code":"IDEMPOTENCY_KEY_REQUIRED","detail":"Send an Idempotency-Key header (up to 200 characters) with each batch.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"VALIDATION":{"summary":"400 VALIDATION","description":"`rows` is missing, empty, or holds more than 20,000 entries; `errors` names the field.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","errors":[{"path":"rows","message":"Too big: expected array to have <=20000 items"}],"correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"SOURCE_REQUIRED":{"summary":"400 SOURCE_REQUIRED","description":"A national bureau sent a batch without `sourceSystem`.","value":{"type":"urn:bc:problem:source_required","title":"Bad request","status":400,"code":"SOURCE_REQUIRED","detail":"Name the national system with ?sourceSystem=.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: The key is missing, mistyped or revoked, or the session has expired.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"The key is missing, mistyped or revoked, or the session has expired.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"403":{"description":"Forbidden. The `code` in the body says which of these it is.\n\n- `FORBIDDEN`: The key was created without the permission this operation needs.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FORBIDDEN":{"summary":"403 FORBIDDEN","description":"The key was created without the permission this operation needs.","value":{"type":"urn:bc:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"You do not have permission to do this.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"422":{"description":"Unprocessable. The `code` in the body says which of these it is.\n\n- `IDEMPOTENCY_KEY_REUSED`: The `Idempotency-Key` was used before for different rows.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"summary":"422 IDEMPOTENCY_KEY_REUSED","description":"The `Idempotency-Key` was used before for different rows.","value":{"type":"urn:bc:problem:idempotency_key_reused","title":"Unprocessable","status":422,"code":"IDEMPOTENCY_KEY_REUSED","detail":"This Idempotency-Key was already used with different content. Use a new key for a new batch.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests for this key. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"Send a batch of certificates","tags":["Send certificates"],"x-integrator":true,"x-permissions":["batch.submit"],"requestBody":{"required":true,"description":"The rows to issue, and an optional label for the batch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"},"examples":{"ghana":{"summary":"Four rows, one of each outcome","description":"The first row is complete. The second is a one-month cover note. The third lacks the policyholder’s address. The fourth has a date that does not exist.","value":{"fileName":"ghana-motor-2026-10-12","rows":[{"policyNumber":"POL-GH-7781204","holderName":"Kojo Essien","holderAddress":"Spintex Road, Accra","holderPhone":"+233244310001","registration":"GR 4821-24","chassisNumber":"JTNBE40K703118426","engineNumber":"1NZ-3492017","make":"Toyota","model":"Corolla","vehicleType":"CAR","use":"PRIVATE","scope":"ECOWAS","territories":"GH,TG,BJ","kind":"CERTIFICATE","validFrom":"2026-10-15","validTo":"2027-10-14"},{"policyNumber":"POL-GH-7781311","holderName":"Esi Quaye Transport Ltd","holderAddress":"Kumasi Road, Techiman","registration":"AS 2210-23","chassisNumber":"WDB9634031L842019","engineNumber":"OM457-9061152","vehicleType":"TRUCK","use":"COMMERCIAL","scope":"NATIONAL","kind":"COVER_NOTE","validFrom":"2026-10-15","validTo":"2026-11-14"},{"policyNumber":"POL-GH-7781205","holderName":"Ama Boateng","registration":"GT 3342-25","chassisNumber":"MHFXW42G603061877","engineNumber":"2KD-5503816","vehicleType":"PICKUP","validFrom":"2026-10-15","validTo":"2027-10-14"},{"policyNumber":"POL-GH-7781206","holderName":"Yaw Darko","holderAddress":"Kwashieman, Accra","registration":"AS 4410-24","chassisNumber":"KMHCT41DAEU612903","engineNumber":"G4FG-2204771","vehicleType":"CAR","validFrom":"2026-10-15","validTo":"31/02/2027"}]}},"single":{"summary":"One certificate","value":{"rows":[{"policyNumber":"POL-GH-7781204","holderName":"Kojo Essien","holderAddress":"Spintex Road, Accra","holderPhone":"+233244310001","registration":"GR 4821-24","chassisNumber":"JTNBE40K703118426","engineNumber":"1NZ-3492017","make":"Toyota","model":"Corolla","vehicleType":"CAR","use":"PRIVATE","scope":"ECOWAS","territories":"GH,TG,BJ","kind":"CERTIFICATE","validFrom":"2026-10-15","validTo":"2027-10-14"}]}}}}}},"security":[{"apiKey":[]},{"bearer":[]}]}},"/api/batches/{id}":{"get":{"description":"Use this to follow a batch you sent that was answered with `202`, or to read the results of any batch again. While `batch.status` is `RECEIVED` or `PROCESSING`, rows are still being worked on and `results` is not present; wait a few seconds between calls.\n\nOnce `batch.status` is `COMPLETED`, `results` lists every row with its outcome and, for an issued row, the certificate number. Results hold no personal data. To see the values read from a held or rejected row, open the batch with the list operations below.\n\n**Access.** Send an integration key or a signed-in session. The caller needs the permission `batch.read`; choose it when you create the key under Integrations → Integration keys.","operationId":"getApiBatch","parameters":[{"name":"id","in":"path","required":true,"description":"The id of the batch, from the `batch.id` of the answer to sending it.","schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"example":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648"}],"responses":{"200":{"description":"The batch. `results` is present once the batch is complete.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProgress"},"examples":{"completed":{"summary":"A completed batch","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"COMPLETED","total":4,"issued":2,"held":1,"rejected":1,"duplicate":0,"pending":0,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":"2026-10-12T08:14:04.907Z"},"results":[{"rowNo":1,"result":"ISSUED","certificateNumber":"GH-2026-0004711","issues":[]},{"rowNo":2,"result":"ISSUED","certificateNumber":"GH-2026-0004712","issues":[]},{"rowNo":3,"result":"HELD","certificateNumber":null,"issues":[{"level":"error","code":"MISSING_FIELD","field":"holderAddress","message":"Missing: policyholder's address."}]},{"rowNo":4,"result":"REJECTED","certificateNumber":null,"issues":[{"level":"error","code":"INVALID_DATE","field":"validTo","message":"\"31/02/2027\" is not a date. Use day/month/year or YYYY-MM-DD."}]}]}},"processing":{"summary":"Still being processed","value":{"batch":{"id":"4f7a9e2b-8c31-4d65-a0b7-3e91d5c2f648","orgId":"0e6b3a14-52c8-5d7f-9a21-8b4f6c0d7e13","orgName":"Volta Assurance","country":"GH","kind":"API","sourceSystem":"INSURER_API","fileName":"ghana-motor-2026-10-12","status":"PROCESSING","total":2000,"issued":840,"held":3,"rejected":12,"duplicate":0,"pending":1145,"createdBy":"key:7d2c91ab-35e0-4f68-b1a4-60c8d2e9f357","createdAt":"2026-10-12T08:14:03.512Z","completedAt":null}}}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `VALIDATION`: A field is missing or has a value the API does not accept; `errors` lists each one.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"VALIDATION":{"summary":"400 VALIDATION","description":"A field is missing or has a value the API does not accept; `errors` lists each one.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: The key is missing, mistyped or revoked, or the session has expired.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"The key is missing, mistyped or revoked, or the session has expired.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"403":{"description":"Forbidden. The `code` in the body says which of these it is.\n\n- `FORBIDDEN`: The key was created without the permission this operation needs.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"FORBIDDEN":{"summary":"403 FORBIDDEN","description":"The key was created without the permission this operation needs.","value":{"type":"urn:bc:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"You do not have permission to do this.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"404":{"description":"Not found. The `code` in the body says which of these it is.\n\n- `NOT_FOUND`: No batch with this id belongs to your organisation.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"NOT_FOUND":{"summary":"404 NOT_FOUND","description":"No batch with this id belongs to your organisation.","value":{"type":"urn:bc:problem:not_found","title":"Not found","status":404,"code":"NOT_FOUND","detail":"The batch was not found.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests for this key. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"Get a batch and its row results","tags":["Send certificates"],"x-integrator":true,"x-permissions":["batch.read"],"security":[{"apiKey":[]},{"bearer":[]}]}},"/feeds":{"get":{"operationId":"FeedsController_list","parameters":[],"responses":{"200":{"description":""}},"tags":["feeds"],"x-permissions":["batch.read"]}},"/feeds/{id}":{"patch":{"operationId":"FeedsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["feeds"],"x-permissions":["feed.manage"]}},"/templates/insurer.csv":{"get":{"description":"Use this to start a bulk file from the right columns. The answer is a CSV file with the header line of the insurer layout and one example row, ready to open in a spreadsheet: replace the example with your policies, keep the header line, save as CSV and send it with **Upload a CSV file**.\n\nThe columns are `policy_number`, `holder_type`, `holder_name`, `holder_address`, `holder_phone`, `holder_email`, `holder_national_id`, `registration`, `chassis_number`, `engine_number`, `make`, `model`, `vehicle_type`, `use`, `scope`, `territories`, `kind`, `valid_from` and `valid_to`. The example row is a certificate that would be issued as it stands, worded for a member state: its usual plate format and its telephone dialling code. It is a Brown Card for that country and three neighbours, for one year.\n\nName the member state with `country`. Without it, the file is worded for the country of the key or session, and for Ghana when the call carries neither. No key is needed to download it, and the answer may be kept for an hour.\n\n**Access.** Open to everyone. A key adds the detail reserved for verifying authorities.","operationId":"downloadInsurerTemplate","parameters":[{"name":"country","in":"query","required":false,"description":"The member state the example row is worded for, as a two-letter code such as `TG`.","schema":{"type":"string","minLength":2,"maxLength":2},"example":"TG"}],"responses":{"200":{"description":"The template as a CSV file, with CRLF line ends.","headers":{"x-correlation-id":{"description":"Identifies this call. It repeats the `x-correlation-id` you sent, or is generated. Quote it to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"content-disposition":{"description":"Names the file.","schema":{"type":"string"},"example":"attachment; filename=\"insurer-bulk-template.csv\""}},"content":{"text/csv":{"schema":{"type":"string"},"examples":{"ghana":{"summary":"Worded for Ghana","value":"policy_number,holder_type,holder_name,holder_address,holder_phone,holder_email,holder_national_id,registration,chassis_number,engine_number,make,model,vehicle_type,use,scope,territories,kind,valid_from,valid_to\r\nMOT-2026-000123,PERSON,Ama Mensah,12 Independence Avenue,+233201234567,ama.mensah@example.com,GHA-123456789-0,GR 4821-24,JTDBR32E720123456,1NZ-4567890,Toyota,Corolla,CAR,PRIVATE,ECOWAS,\"GH,TG,BJ,NG\",CERTIFICATE,2026-11-01,2027-10-31\r\n"},"togo":{"summary":"Worded for Togo (`?country=TG`)","value":"policy_number,holder_type,holder_name,holder_address,holder_phone,holder_email,holder_national_id,registration,chassis_number,engine_number,make,model,vehicle_type,use,scope,territories,kind,valid_from,valid_to\r\nMOT-2026-000123,PERSON,Ama Mensah,12 Independence Avenue,+22820123456,ama.mensah@example.com,,TG 1234 AB,JTDBR32E720123456,1NZ-4567890,Toyota,Corolla,CAR,PRIVATE,ECOWAS,\"TG,GH,BJ,NG\",CERTIFICATE,2026-11-01,2027-10-31\r\n"}}}}},"400":{"description":"Bad request. The `code` in the body says which of these it is.\n\n- `VALIDATION`: A field is missing or has a value the API does not accept; `errors` lists each one.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"VALIDATION":{"summary":"400 VALIDATION","description":"A field is missing or has a value the API does not accept; `errors` lists each one.","value":{"type":"urn:bc:problem:validation","title":"Bad request","status":400,"code":"VALIDATION","detail":"Some fields are missing or invalid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"401":{"description":"Unauthorized. The `code` in the body says which of these it is.\n\n- `UNAUTHENTICATED`: A key or session was sent but is not valid. Leave the credentials out to call as anyone.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"UNAUTHENTICATED":{"summary":"401 UNAUTHENTICATED","description":"A key or session was sent but is not valid. Leave the credentials out to call as anyone.","value":{"type":"urn:bc:problem:unauthenticated","title":"Unauthorized","status":401,"code":"UNAUTHENTICATED","detail":"Your session has expired or the key is not valid.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}},"429":{"description":"Too many requests. The `code` in the body says which of these it is.\n\n- `RATE_LIMITED`: Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","headers":{"x-correlation-id":{"description":"Quote this to support.","schema":{"type":"string"},"example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"retry-after":{"description":"Seconds to wait before the next call.","schema":{"type":"integer"},"example":42}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"RATE_LIMITED":{"summary":"429 RATE_LIMITED","description":"Too many calls in a minute. Wait for the number of seconds in `retry-after`, then continue.","value":{"type":"urn:bc:problem:rate_limited","title":"Too many requests","status":429,"code":"RATE_LIMITED","detail":"Too many requests from this address. Try again in a minute.","correlationId":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"}}}}}}},"summary":"Download the insurer file template","tags":["Upload a file"],"x-integrator":true,"x-public":true,"security":[{},{"apiKey":[]},{"bearer":[]}]}}},"info":{"title":"Integration service","description":"National motor-database feeds, bulk files and API batches with per-row results, and the regional API registry.","version":"1.0.0","contact":{}},"tags":[{"name":"Send certificates","description":"Issue many certificates in one call from your own policy system, and learn how every row ended. Each row describes one policy, one vehicle and one period of insurance."},{"name":"Follow your batches","description":"Every file and every API call becomes a batch. List your batches, open one, and read the result of each row: issued, held for a missing item, rejected for an invalid value, or a duplicate of a certificate already issued."},{"name":"Upload a file","description":"Send a CSV file. The file follows the same rules and produces the same batch and row results."}],"servers":[],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"An integration key, created under Integrations → Integration keys. It looks like `bc_1a2b3c4d_…` and is shown once, when you create it."},"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http","description":"An integration key sent as `Authorization: Bearer bc_…`, or the session token of a signed-in user."}},"schemas":{"BatchPage":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Batch"}},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Batches that match, across all pages."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","total","limit","offset"],"description":"A page of batches, newest first."},"Batch":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The batch id. Use it to poll or to open the batch."},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The organisation that sent the batch."},"orgName":{"nullable":true,"type":"string"},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 code of the country."},"kind":{"type":"string","enum":["CSV","API","NATIONAL_FEED"],"description":"`API` for a JSON call, `CSV` for an uploaded file, `NATIONAL_FEED` for a bureau’s national file."},"sourceSystem":{"type":"string","description":"The layout the rows were read in.","example":"INSURER_API"},"fileName":{"nullable":true,"description":"The `fileName` you sent, if any.","type":"string"},"status":{"type":"string","enum":["RECEIVED","PROCESSING","COMPLETED","FAILED"],"description":"`RECEIVED` and `PROCESSING` mean rows are still being worked on. `COMPLETED` means every row has a final result or is held. `FAILED` means processing was interrupted; sending the same rows again resumes it."},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows in the batch."},"issued":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows that produced a certificate."},"held":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows kept for a missing item or a temporary problem."},"rejected":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows with an invalid value."},"duplicate":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows for a certificate that already exists."},"pending":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows not yet processed."},"skipped":{"type":"object","properties":{"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lines":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}}},"required":["count","lines"]},"ignoredColumns":{"description":"Uploaded files only: header names that were not recognised and so were not read.","type":"array","items":{"type":"string"}},"createdBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"completedAt":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["id","orgId","orgName","country","kind","sourceSystem","fileName","status","total","issued","held","rejected","duplicate","pending","createdBy","createdAt","completedAt"],"description":"A batch of certificates and how far it has got."},"FileUpload":{"type":"object","properties":{"csv":{"type":"string","minLength":1,"description":"The whole file as text."}},"required":["csv"],"description":"The file as JSON."},"FileReceipt":{"type":"object","properties":{"batch":{"$ref":"#/components/schemas/Batch"},"alreadyReceived":{"type":"boolean","description":"True when this exact file was already received: nothing was issued a second time."}},"required":["batch","alreadyReceived"],"description":"The answer to uploading a file."},"BatchDetail":{"type":"object","properties":{"batch":{"$ref":"#/components/schemas/Batch"},"rows":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BatchRow"}},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Rows that match the filter, across all pages."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","total","limit","offset"]}},"required":["batch","rows"],"description":"A batch with a page of its rows."},"BatchRow":{"type":"object","properties":{"rowNo":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"result":{"type":"string","enum":["PENDING","ISSUED","HELD","REJECTED","DUPLICATE"],"description":"`ISSUED`: a certificate was created. `HELD`: an Art. 15(4) item is missing, credits ran out, another insurer covers the vehicle on those days or a service was unavailable; the row is kept, never dropped. `REJECTED`: a value is invalid, or the insurer is suspended. `DUPLICATE`: the same policy, vehicle and start date was already issued. `PENDING`: not yet processed."},"policyNumber":{"nullable":true,"type":"string"},"registration":{"nullable":true,"type":"string"},"holderName":{"nullable":true,"type":"string"},"validFrom":{"nullable":true,"type":"string"},"validTo":{"nullable":true,"type":"string"},"certificateId":{"nullable":true,"type":"string"},"certificateNumber":{"nullable":true,"type":"string"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/RowIssue"}},"correctedAt":{"nullable":true,"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"fields":{"description":"On a held or rejected row: the values as read, so you can see what to correct.","type":"object","additionalProperties":{"type":"string"}}},"required":["rowNo","result","policyNumber","registration","holderName","validFrom","validTo","certificateId","certificateNumber","issues","correctedAt"],"description":"One row of a batch, with the values read from it."},"RowIssue":{"type":"object","properties":{"level":{"type":"string","enum":["error","warning"],"description":"`error` stops the row from being issued. `warning` is recorded and the row is still issued."},"code":{"type":"string","description":"A stable code. Row codes include `MISSING_FIELD`, `INVALID_DATE`, `PERIOD_ORDER`, `PERIOD_TOO_LONG`, `COVER_NOTE_TOO_LONG`, `PERIOD_ENDED`, `INVALID_SCOPE`, `INVALID_KIND`, `UNKNOWN_TERRITORY`, `INVALID_REGISTRATION`, `STOCK_EMPTY`, `REGISTRY_UNAVAILABLE`, `VEHICLE_ALREADY_COVERED`, `INSURER_SUSPENDED`, `INSURER_NOT_IN_GOOD_STANDING`, `DUPLICATE_ROW`, and the warnings `PLATE_FORMAT`, `VEHICLE_TYPE_UNKNOWN`, `EMAIL_IGNORED`.","example":"MISSING_FIELD"},"field":{"description":"The row field the issue is about.","example":"holderAddress","type":"string"},"message":{"type":"string","description":"A plain sentence that can be shown to a person.","example":"Missing: policyholder's address."}},"required":["level","code","message"],"description":"One thing found while checking a row."},"BatchRequest":{"type":"object","properties":{"rows":{"minItems":1,"maxItems":20000,"type":"array","items":{"$ref":"#/components/schemas/BatchRowInput"},"description":"The certificates to issue, 1 to 20,000 rows. Every value is a string."},"fileName":{"description":"A label for the batch, shown with it in your batch list.","example":"ghana-motor-2026-10-12","type":"string","maxLength":200}},"required":["rows"],"description":"The rows of a batch."},"BatchRowInput":{"type":"object","properties":{"policyNumber":{"type":"string","description":"Your policy reference. Required to issue: a row without it is held.","example":"POL-GH-7781204"},"holderType":{"description":"Worked out from the name when left out: names with words such as Ltd, Limited or Transport are companies.","type":"string","enum":["PERSON","COMPANY"]},"holderName":{"type":"string","description":"The policyholder’s full name, or the company name. Required to issue.","example":"Kojo Essien"},"holderAddress":{"type":"string","description":"The policyholder’s address. Required to issue: it is printed on every certificate.","example":"Spintex Road, Accra"},"holderPhone":{"description":"International format. The holder receives a link to the certificate by SMS.","example":"+233244310001","type":"string"},"holderEmail":{"description":"An address that cannot be read is ignored, with a warning.","example":"kojo.essien@example.org","type":"string"},"holderNationalId":{"description":"Where the country asks for it, for example the Ghana Card number.","example":"GHA-722145908-3","type":"string"},"registration":{"type":"string","description":"The registration plate as printed. Required to issue. A plate outside the country’s usual pattern is accepted with a warning.","example":"GR 4821-24"},"chassisNumber":{"type":"string","description":"Chassis number or VIN. Required to issue.","example":"JTNBE40K703118426"},"engineNumber":{"type":"string","description":"The engine number. Required to issue.","example":"1NZ-3492017"},"make":{"example":"Toyota","type":"string"},"model":{"example":"Corolla","type":"string"},"vehicleType":{"type":"string","description":"Required to issue. One of `CAR`, `TAXI`, `MINIBUS`, `BUS`, `TRUCK`, `TRAILER`, `MOTORCYCLE`, `TRICYCLE`, `VAN`, `PICKUP`, `TRACTOR`, `OTHER`. Common words such as \"saloon\", \"pick up\" or \"trotro\" are understood.","example":"CAR"},"use":{"description":"One of `PRIVATE`, `COMMERCIAL`, `HIRE`, `GOVERNMENT`, `MOTOR_TRADE`.","example":"PRIVATE","type":"string"},"scope":{"description":"`NATIONAL` covers the issuing country. `ECOWAS` is the Brown Card and covers `territories`. Empty means `NATIONAL`.","example":"ECOWAS","type":"string"},"territories":{"description":"With `ECOWAS`: member-state codes separated by commas, for example `GH,TG,BJ`. The issuing country must be included. Empty means all fifteen member states.","example":"GH,TG,BJ","type":"string"},"kind":{"description":"`CERTIFICATE` (the default) or `COVER_NOTE`. A cover note is valid for one month at most.","example":"CERTIFICATE","type":"string"},"validFrom":{"type":"string","description":"First day of the period of insurance, as `YYYY-MM-DD` or `DD/MM/YYYY`. Required to issue.","example":"2026-10-15"},"validTo":{"type":"string","description":"Last day of the period of insurance, in the same formats. Required to issue. A certificate runs for two years at most and a cover note for one month.","example":"2027-10-14"}},"description":"One certificate to issue. Names may be camelCase, as here, or snake_case, as in a CSV file: `policy_number`, `holder_name`, `valid_from`."},"BatchSubmission":{"type":"object","properties":{"batch":{"$ref":"#/components/schemas/Batch"},"results":{"description":"One entry per row. Present when the batch finished within the call.","type":"array","items":{"$ref":"#/components/schemas/RowResult"}},"pollUrl":{"description":"Present when the batch continues in the background: poll this path.","type":"string"},"alreadyReceived":{"type":"boolean","description":"True when these rows were already received: nothing was issued a second time."}},"required":["batch","alreadyReceived"],"description":"The answer to sending a batch."},"RowResult":{"type":"object","properties":{"rowNo":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position of the row in your `rows` array, starting at 1."},"result":{"type":"string","enum":["PENDING","ISSUED","HELD","REJECTED","DUPLICATE"],"description":"`ISSUED`: a certificate was created. `HELD`: an Art. 15(4) item is missing, credits ran out, another insurer covers the vehicle on those days or a service was unavailable; the row is kept, never dropped. `REJECTED`: a value is invalid, or the insurer is suspended. `DUPLICATE`: the same policy, vehicle and start date was already issued. `PENDING`: not yet processed."},"certificateNumber":{"nullable":true,"description":"The number of the certificate, for an issued or duplicate row.","type":"string"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/RowIssue"}}},"required":["rowNo","result","certificateNumber","issues"],"description":"How one row of a batch ended. Holds no personal data."},"BatchProgress":{"type":"object","properties":{"batch":{"$ref":"#/components/schemas/Batch"},"results":{"description":"One entry per row. Present once the batch is complete.","type":"array","items":{"$ref":"#/components/schemas/RowResult"}}},"required":["batch"],"description":"A batch and, once complete, the result of each row."},"Problem":{"type":"object","description":"An error, as RFC 9457 problem details. Show `detail` to people; branch on `code` in code.","required":["type","title","status","code","detail","correlationId"],"properties":{"type":{"type":"string","description":"Identifies the kind of problem: `urn:bc:problem:` followed by `code` in lower case.","example":"urn:bc:problem:stock_empty"},"title":{"type":"string","description":"The name of the HTTP status.","example":"Conflict"},"status":{"type":"integer","description":"The HTTP status.","example":409},"code":{"type":"string","description":"A stable machine-readable code. Codes are never reused for another meaning.","example":"STOCK_EMPTY"},"detail":{"type":"string","description":"A plain sentence that can be shown to a person.","example":"No certificate credits are left."},"correlationId":{"type":"string","description":"Quote this when you contact support. The same value is in the `x-correlation-id` response header.","example":"9f2b1c64-3d0e-4a57-b8c1-6e7d5a0f4b21"},"errors":{"type":"array","description":"Present on validation errors: one entry per field that needs attention.","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string","description":"The field, for example `holder.address`."},"message":{"type":"string","description":"What to correct."}}}}}}}}}