This guide shows how to create a report, read it, update it, and find it again from your own systems. Every call names the report by an identifier; the section below explains the two identifiers you can use.
All examples use the production base URL https://api.kalepa.com and a token in $TOKEN. See Getting started for the token.
The two identifiers
| Identifier | Who assigns it | Shape | Example |
|---|---|---|---|
| Kalepa report ID | Kalepa, when the report is created | UUID | f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3 |
Your external_id | You, on create or later with PATCH | A non-empty string (see the path rules below) | ACME-2026-000123 |
The values ACME-2026-000123, COMMERCIAL, and EAST in this guide are examples. Replace them with your own submission number, org group, and sub-org group.
Both identifiers work in the {report_id} path segment of these endpoints:
GET /external/reports/{report_id}PATCH /external/reports/{report_id}GET /external/reports/{report_id}/filesPUT /external/reports/{report_id}/files/{uploaded_file_id}andDELETE /external/reports/{report_id}/files/{uploaded_file_id}(only{report_id}takes your identifier;{uploaded_file_id}is theidKalepa returned when you uploaded the file)
— see Work with report filesPOST /external/reports/{report_id}/client-data— see Work with client data- The policy tower:
/external/reports/{report_id}/policies,.../policies/{policy_id},.../policies/{policy_id}/coverages,.../policies/{policy_id}/premiums,.../coverages/{coverage_id},.../coverages/{coverage_id}/limits,.../coverages/{coverage_id}/deductibles,.../coverages/{coverage_id}/premiums,.../limits/{limit_id},.../deductibles/{deductible_id}and.../premiums/{premium_id}(only{report_id}takes your identifier; the policy, coverage, limit, deductible and premium ids are theidvalues Kalepa returned)
Two more endpoints take the report identifier in the request body or form instead of the path, and accept both identifiers there:
POST /external/files— thereport_idform fieldPOST /external/files/upload_url— thereport_idbody field
— see Work with report files
GET /external/report_id goes the other way: you give it your external_id and it returns the Kalepa report ID. See Look up the Kalepa report ID from an external_id.
Every other report endpoint takes the Kalepa report ID only. That includes notes, submission notes, status, extract, and exports. On those endpoints an external_id is rejected as an invalid value.
The API detects the identifier type from the shape of the value:
- A UUID-shaped value is read as the Kalepa report ID. A dashed UUID, 32 hexadecimal characters, a brace-wrapped UUID, and the
urn:uuid:form all count as UUID-shaped. - Any other value is read as your
external_id.
Use the id_type query parameter to force one reading. See the section "Force the identifier type with id_type" below.
Create a report with your external_id
external_idSend external_id in the body of POST /external/reports.
curl -X POST "https://api.kalepa.com/external/reports" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Manufacturing, Inc.",
"external_id": "ACME-2026-000123",
"businesses": [
{
"requested_name": "Acme Manufacturing, Inc.",
"requested_address": "123 Main St, Springfield, IL 62701"
}
]
}'
Example response (201 Created):
{
"id": "f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3",
"name": "Acme Manufacturing, Inc."
}
Store the id value. It is the Kalepa report ID. It never changes and is never ambiguous.
An external_id identifies one report inside your organization. POST /external/reports is idempotent by external_id: if a non-deleted report already has the value you send, the API returns that existing report with 201 and does not create another one. Check the id in the response when you expect a new report. On PATCH, setting external_id to a value that another non-deleted report already has returns 409; sending the report's current value does not.
Read a report
By Kalepa report ID
curl "https://api.kalepa.com/external/reports/f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3" \
-H "Authorization: Bearer $TOKEN"
By your external_id
external_idPut the external_id in the path. The value is not UUID-shaped, so the API reads it as your external_id.
curl "https://api.kalepa.com/external/reports/ACME-2026-000123" \
-H "Authorization: Bearer $TOKEN"
Both calls return the same report. Example response (200 OK):
{
"id": "f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3",
"name": "Acme Manufacturing, Inc.",
"status": "Quoted"
}
Besides the ID, the name, and the status, the response has summary fields such as the stage, the assigned underwriters, the broker and brokerage, the recommendation score, and the key dates. The example above shows a subset.
What an external_id lookup can match
external_id lookup can matchAn external_id lookup follows these rules:
- The lookup only searches the reports available to your API client.
- Deleted reports never match.
- If several reports share the
external_id, archived reports are dropped when at least one non-archived report matches. - If more than one report still matches, the request fails with error code
AMBIGUOUS_IDENTIFIER: status400, or422on the policy-tower endpoints. See the section "Errors" below. - If no report matches, the request fails with
404.
Force the identifier type with id_type
id_typeAdd id_type to the query string to override the auto-detection:
id_type | Meaning |
|---|---|
| Omitted | Auto-detect by shape. |
uuid | Read the path value as the Kalepa report ID. |
external_id | Read the path value as your external_id. |
Use id_type=external_id when your own identifier is UUID-shaped. Without it, the API reads the value as a Kalepa report ID.
curl "https://api.kalepa.com/external/reports/3f2b9c0e-8a1d-4c6f-9e2a-7b5d1c4e8f90?id_type=external_id" \
-H "Authorization: Bearer $TOKEN"
Use id_type=uuid when you want a guarantee that the API never reads the value as an external_id. The request then fails with 400 if the path value is not a valid UUID.
curl "https://api.kalepa.com/external/reports/f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3?id_type=uuid" \
-H "Authorization: Bearer $TOKEN"
Any id_type value other than uuid or external_id returns 400.
Update a report
PATCH /external/reports/{report_id} accepts both identifiers in the path. The body is the same in both cases.
By Kalepa report ID
curl -X PATCH "https://api.kalepa.com/external/reports/f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Manufacturing, Inc. (renewal)"}'
By your external_id
external_idcurl -X PATCH "https://api.kalepa.com/external/reports/ACME-2026-000123" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Manufacturing, Inc. (renewal)"}'
Example response for both calls (200 OK):
{
"id": "f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3",
"name": "Acme Manufacturing, Inc. (renewal)"
}
Set or change the external_id on an existing report
external_id on an existing reportSend external_id in the PATCH body. Target the report by its Kalepa report ID.
curl -X PATCH "https://api.kalepa.com/external/reports/f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"external_id": "ACME-2026-000123"}'
The API returns 409 if another non-deleted report in your organization already has this external_id. Sending the report's current external_id again does not conflict.
A 429 response means another update holds a lock on the report. Wait a short time and retry.
Correct the insured's name or address
Send requested_name or requested_address in the PATCH body when the insured's name or address changes in your system. Each value replaces the matching requested value of the report's first named insured. The business Kalepa matched the insured to is not changed.
curl -X PATCH "https://api.kalepa.com/external/reports/ACME-2026-000123" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"requested_name": "Acme Manufacturing, Inc.", "requested_address": "500 Oak Ave, Springfield, IL 62704"}'
You can send one field or both. When the report has no first named insured yet but exactly one business, that business is updated. The API returns 400 when the report has no business to update. A null or empty value is ignored.
Look up the Kalepa report ID from an external_id
external_idGET /external/report_id returns the Kalepa report ID for one external_id. Use it when you cannot put the external_id in a path (see the section "Reserved values and URL encoding" below), or when you want to store the Kalepa report ID.
curl "https://api.kalepa.com/external/report_id?external_id=ACME-2026-000123" \
-H "Authorization: Bearer $TOKEN"
Example response (200 OK):
{
"report_id": "f116fa5c-e5d9-43c2-8dc3-ac3fa60ff3b3"
}
Rules for this endpoint:
- The value is always read as your
external_id, also when it is UUID-shaped. There is noid_typeparameter. - The same match rules apply as for a path lookup. See What an
external_idlookup can match. org_groupandsub_org_groupare accepted as query parameters.- Only API clients (machine users) can call this endpoint. Other authenticated callers receive
403.
Narrow a lookup with org_group and sub_org_group
org_group and sub_org_groupIf you reuse an external_id across org groups, add org_group or sub_org_group to the query string. Both parameters are exact-match filters on the report's org_group and sub_org_group.
The parameters only apply when you target the report by external_id. They have no effect when you target by Kalepa report ID.
| Parameter form | Meaning |
|---|---|
| Parameter omitted | No filter. |
Empty value (?org_group=) | Only reports with no org_group. |
Value (?org_group=COMMERCIAL) | Only reports whose org_group is exactly COMMERCIAL. |
The same three forms apply to sub_org_group.
Read a report by external_id in one org group and sub-org group:
curl "https://api.kalepa.com/external/reports/ACME-2026-000123?org_group=COMMERCIAL&sub_org_group=EAST" \
-H "Authorization: Bearer $TOKEN"
Read a report by external_id that has no org group:
curl "https://api.kalepa.com/external/reports/ACME-2026-000123?org_group=" \
-H "Authorization: Bearer $TOKEN"
Update a report by external_id in one org group:
curl -X PATCH "https://api.kalepa.com/external/reports/ACME-2026-000123?org_group=COMMERCIAL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Manufacturing, Inc. (bound)"}'
Look up the Kalepa report ID in one org group:
curl "https://api.kalepa.com/external/report_id?external_id=ACME-2026-000123&org_group=COMMERCIAL" \
-H "Authorization: Bearer $TOKEN"
If the filters still leave more than one report, the API returns error code AMBIGUOUS_IDENTIFIER (status 400, or 422 on the policy-tower endpoints). In that case, use the Kalepa report ID.
Reserved values and URL encoding
URL-encode reserved characters in an external_id before you put it in a path or a query string. Examples: a space becomes %20, # becomes %23, & becomes %26.
curl "https://api.kalepa.com/external/reports/ACME%202026%20000123" \
-H "Authorization: Bearer $TOKEN"
Two values cannot go in the {report_id} path segment:
| Value | Reason | What to do |
|---|---|---|
An external_id that contains / | The path splits on /. | Call GET /external/report_id?external_id=<encoded value> to get the Kalepa report ID. Then use the Kalepa report ID in the path. |
The exact value extract on GET | GET /external/reports/extract is a different endpoint. | Call GET /external/report_id?external_id=extract to get the Kalepa report ID. Then use the Kalepa report ID in the path. |
Example for an external_id with a slash, ACME/2026/000123:
curl "https://api.kalepa.com/external/report_id?external_id=ACME%2F2026%2F000123" \
-H "Authorization: Bearer $TOKEN"
Errors
| Status | When | What to do |
|---|---|---|
400 | id_type=uuid and the path value is not a valid UUID. | Send a valid Kalepa report ID, or remove id_type. |
400 | id_type is not uuid or external_id. | Use one of the two values, or remove id_type. |
400 with code AMBIGUOUS_IDENTIFIER | The external_id matches more than one report. | Add org_group or sub_org_group, or use the Kalepa report ID. |
400 | PATCH sends requested_name or requested_address for a report with no business to update. | Check the report's businesses in the Kalepa UI, then retry. |
400 | PATCH sends a client_status that matches none of your organization's configured statuses. The match is exact and case-sensitive after trimming surrounding whitespace. The request changes nothing. | Use a status from the allowed list in the error message, then retry. |
422 with code AMBIGUOUS_IDENTIFIER | Policy-tower endpoints only (/external/reports/{report_id}/policies...): the external_id matches more than one report. | Add org_group or sub_org_group, or use the Kalepa report ID. |
401 | The token is missing, expired, or invalid. | Request a new token. |
403 | Your API client is not permitted to act on this report. Also returned on GET /external/report_id when the caller is not an API client. | Check the client's scope with Kalepa. |
404 | No report available to your API client matches the identifier and filters. | Check the identifier and the org_group / sub_org_group filters. |
409 | PATCH sets external_id to a value that another non-deleted report in your organization already has. | Use a different external_id, or update the report that already has it. |
429 | PATCH only. Another update holds a lock on the report. | Wait a short time and retry. |
The AMBIGUOUS_IDENTIFIER response has this body:
{
"detail": {
"code": "AMBIGUOUS_IDENTIFIER",
"message": "The external_id matches more than one report. Narrow with org_group / sub_org_group, or use the Kalepa report ID."
}
}
Check detail.code in your code. The message text can change.
Recommendations
- Store the Kalepa report ID that
POST /external/reportsreturns. Use it for all later calls when you can. - Keep each
external_idunique inside your organization. Then a lookup byexternal_idis never ambiguous. - Send
external_idon create. Then the report has your identifier from the start. - If your identifiers are UUID-shaped, always send
id_type=external_idwhen you target byexternal_id.