Walk-throughs
Lifecycles
Concrete request/response chains for the most common partner workflows — endorsement, cancellation, renewal, rewrite, audit, extension, and out-of-sequence rebase.
Every transaction below uses the same quote-then-bind pattern:
POST creates a priced draft, GET reads it
back, POST …/bind commits with an
Idempotency-Key. Bodies are illustrative — UUIDs are made up,
and real responses include a few extra audit fields (created_at,
updated_at, x-request-id correlation) elided here
for clarity. See Idempotency and
Errors for the contract details.
Drafts expire — quote again after the TTL:
| Resource | Draft TTL |
|---|---|
endorsement, cancellation, extension, audit, oos-rebase | 24 hours |
renewal, rewrite | 7 days |
Endorsement: bump GL tower from $5M to $10M
Insured asks for higher excess limits mid-term. You quote the change, the
underwriter confirms with the insured, then you bind. Server is
authoritative on premium — the partner describes the change, the server
pro-rates against the policy's FactorVersion.
1. Create the priced draft
Quote the change with POST /endorsements.
POST /endorsements
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"effective_date": "2026-08-15",
"description": "Bump GL tower to $10M per insured request",
"changes": [
{ "op": "change_limit", "tower_id": "twr_GL01-0004-4e8b-9988-4e7f60ad3233", "new_limit": 10000000 }
]
}
HTTP/1.1 201 Created
{
"endorsement_id": "end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"status": "draft",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"effective_date": "2026-08-15",
"pro_rata_factor": 0.7945, // 290 of 365 days remain
"system_premium": 3973.00, // server-computed delta for the partial term
"full_term_delta": 5000.00, // delta as if effective for the whole term
"new_written_premium": 28973.00,
"factor_version": "fv_2026Q3_GL_v1",
"valid_until": "2026-08-16T15:42:11Z", // 24h TTL
"links": {
"self": "/endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"bind": "/endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344/bind",
"abandon": "/endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344"
}
} 2. Read it back later (optional)
GET /endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344
HTTP/1.1 200 OK
{
"endorsement_id": "end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"status": "draft",
"system_premium": 3973.00,
"valid_until": "2026-08-16T15:42:11Z"
/* …same shape as the create response… */
} 3. Mutate and re-rate (optional)
Endorsement is the only resource that supports PATCH. Mutating
the payload re-rates the draft and resets the TTL.
PATCH /endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344
{
"changes": [
{ "op": "change_limit", "tower_id": "twr_GL01-0004-4e8b-9988-4e7f60ad3233", "new_limit": 7500000 }
]
}
HTTP/1.1 200 OK
{
"endorsement_id": "end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"status": "draft",
"system_premium": 1986.00, // re-rated
"valid_until": "2026-08-16T16:01:42Z" // TTL reset
} 4. Bind
The bind step requires Idempotency-Key. Generate a fresh UUID
per attempt; replays return the original response byte-for-byte.
POST /endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344/bind
Idempotency-Key: 9f3c8c2e-7a02-4d12-8b91-72e0fbc01a5e
{}
HTTP/1.1 200 OK
{
"endorsement_id": "end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"status": "bound",
"transaction_id": "transaction_2b9efa07-2707-4880-b110-c0e1d710998a",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"sequence_number": 7,
"premium_change": 1986.00,
"new_total_premium": 26986.00,
"bound_at": "2026-08-15T16:02:14Z"
} 5. Verify on the audit trail
The bound transaction appears on GET /get-policy-transactions.
GET /get-policy-transactions?policy_id=pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100
HTTP/1.1 200 OK
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"count": 7,
"transactions": [
/* …earlier rows… */
{
"id": "transaction_2b9efa07-2707-4880-b110-c0e1d710998a",
"sequence_number": 7,
"transaction_type": "endorsement",
"effective_date": "2026-08-15",
"premium_change": 1986.00,
"new_total_premium": 26986.00,
"endorsement_details": {
"changes": [{ "op": "change_limit", "tower_id": "twr_GL01-0004-4e8b-9988-4e7f60ad3233", "new_limit": 7500000 }]
},
"created_at": "2026-08-15T16:02:14Z"
}
]
} Abandon (alternative ending)
If the insured rejects the quote before you bind, just abandon the draft.
Only allowed while status=draft.
DELETE /endorsements/end_b8e2c10a-1101-46d2-8c4f-5f8071be4344
HTTP/1.1 204 No Content A GET on the same id afterwards returns the tombstone:
{
"endorsement_id": "end_b8e2c10a-1101-46d2-8c4f-5f8071be4344",
"status": "abandoned",
"abandoned_at": "2026-08-15T16:30:01Z"
} Stale snapshot (concurrent change)
If another transaction was applied to the policy between your draft
creation and bind (someone else endorsed, your bind raced the policy
calendar job, etc.), bind returns 409 stale-snapshot with a
fresh draft already created against the new state — supersede and retry,
no need to re-create. Your retry budget is one bind on the
fresh_draft id.
HTTP/1.1 409 Conflict
{
"type": "https://skadispecialty.com/api/problems/stale-snapshot",
"title": "Snapshot drifted; bind a fresh draft",
"status": 409,
"detail": "The policy state changed between draft creation and bind.",
"fresh_draft": {
"endorsement_id": "end_3c4d5e6f-1102-46d2-8c4f-7a8b9c0d1e2f",
"status": "draft",
"system_premium": 2104.00, // re-rated against the new snapshot
"valid_until": "2026-08-16T16:30:00Z",
"links": { "bind": "/endorsements/end_3c4d5e6f-…/bind" }
}
} Cancellation: pro-rata refund with notice
Insured wants out at the end of next month, with 30 days notice. The
future notice_date moves the policy to
pending_cancel; the daily calendar job advances it to
cancelled on cancellation_date.
1. Create the cancellation draft
POST /cancellations
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"cancellation_date": "2026-09-30",
"method": "pro_rata",
"notice_date": "2026-08-31", // → pending_cancel until 09-30
"reason_code": "INSURED_REQ"
}
HTTP/1.1 201 Created
{
"cancellation_id": "can_91dc4321-1202-46e3-9d5b-6c9182cf5455",
"status": "draft",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"method": "pro_rata",
"is_noc": true,
"notice_date": "2026-08-31",
"cancellation_date": "2026-09-30",
"return_premium": 8245.00, // refund to insured (pro-rata of unearned)
"mep_floor": 6250.00, // 25% MEP floor on the original 25,000 written
"short_rate_penalty": 0, // method=pro_rata, not short_rate
"commission_clawback": 989.40,
"valid_until": "2026-08-16T17:14:02Z",
"links": {
"bind": "/cancellations/can_91dc4321-1202-46e3-9d5b-6c9182cf5455/bind",
"abandon": "/cancellations/can_91dc4321-1202-46e3-9d5b-6c9182cf5455"
}
} 2. Bind
POST /cancellations/can_91dc4321-1202-46e3-9d5b-6c9182cf5455/bind
Idempotency-Key: 7e0f2b89-4d31-4a02-9c11-83a7e0c4d12f
{}
HTTP/1.1 200 OK
{
"cancellation_id": "can_91dc4321-1202-46e3-9d5b-6c9182cf5455",
"status": "bound",
"transaction_id": "transaction_8a9bc0de-3808-4990-c220-d1f2e8210ab1",
"policy_status": "pending_cancel", // calendar job will flip → cancelled on 09-30
"premium_change": -8245.00,
"return_premium": 8245.00,
"new_total_premium": 16755.00,
"bound_at": "2026-08-16T17:18:45Z"
} Variant: short-rate penalty
If the partner's contract requires short-rate (penalty for early
termination), swap method to short_rate. The
short_rate_penalty field becomes non-zero and reduces the
return_premium accordingly. The MEP floor still applies.
Response trimmed to the fields that differ:
POST /cancellations
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"cancellation_date": "2026-09-30",
"method": "short_rate",
"reason_code": "INSURED_REQ"
}
HTTP/1.1 201 Created
{
"method": "short_rate",
"return_premium": 6750.00,
"short_rate_penalty": 1495.00,
"mep_floor": 6250.00
/* …other fields as in the pro-rata example… */
} Renewal: successor at next year's filed rates
The renewal endpoint is the compliance unlock — server rates the successor
at whichever FactorVersion is effective on
new_effective_date. Filed rate revisions automatically apply
when their effective_from date arrives.
1. Create the renewal draft
POST /renewals
{
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"new_effective_date": "2027-01-15",
"new_expiration_date": "2028-01-15"
}
HTTP/1.1 201 Created
{
"renewal_id": "rnw_5fb3d9e4-1404-45ff-acdc-8ebba4e17677",
"status": "draft",
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"new_effective_date": "2027-01-15",
"new_expiration_date": "2028-01-15",
"successor_premium": 27800.00, // new policy's full-term premium
"factor_version": "fv_2027Q1_GL_v1", // current at 2027-01-15
"valid_until": "2026-08-23T17:30:00Z", // 7d TTL
"links": {
"bind": "/renewals/rnw_5fb3d9e4-1404-45ff-acdc-8ebba4e17677/bind",
"abandon": "/renewals/rnw_5fb3d9e4-1404-45ff-acdc-8ebba4e17677"
}
} 2. Bind atomically
Bind emits a renewal transaction on the prior policy (status →
renewed), creates the successor with status=bound
and a prior_policy_id back-reference. Both writes live in one
DB transaction.
POST /renewals/rnw_5fb3d9e4-1404-45ff-acdc-8ebba4e17677/bind
Idempotency-Key: 5b6c7d8e-9f01-4234-a567-89bcdef01234
{}
HTTP/1.1 200 OK
{
"renewal_id": "rnw_5fb3d9e4-1404-45ff-acdc-8ebba4e17677",
"status": "bound",
"transaction_id": "transaction_3c4d5e6f-4909-4a01-9221-e2031c7e1bcd",
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"successor_policy_id": "pol_b8c2d3e4-2002-4d6e-9c33-2c9b5e8d1234",
"successor_premium": 27800.00,
"bound_at": "2026-08-16T17:35:12Z"
} 3. Read the successor through the policies endpoint
The successor is a first-class policy on GET /get-policies.
GET /get-policies?id=pol_b8c2d3e4-2002-4d6e-9c33-2c9b5e8d1234
HTTP/1.1 200 OK
{
"policy": {
"id": "pol_b8c2d3e4-2002-4d6e-9c33-2c9b5e8d1234",
"policy_number": "ODN-P-2027-000128",
"status": "bound",
"effective_date": "2027-01-15",
"expiration_date": "2028-01-15",
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"written_premium": 27800.00
}
} Rewrite: off-cycle replacement
Mid-term restructure — terminate the prior policy and reissue a successor. Same successor-rating semantics as renewal. Named-insured changes also flow through here per Guidewire / Duck Creek convention.
1. Quote the rewrite
POST /rewrites
{
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"rewrite_date": "2026-10-01",
"new_expiration_date": "2027-10-01",
"reason": "Restructure attachment from $1M to $2M",
"new_named_insured": "Acme Restaurants Holdings LLC"
}
HTTP/1.1 201 Created
{
"rewrite_id": "rwt_6cc4ea05-1505-46dd-bedd-9fccb5f28788",
"status": "draft",
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"rewrite_date": "2026-10-01",
"new_expiration_date": "2027-10-01",
"successor_premium": 31200.00,
"factor_version": "fv_2026Q4_GL_v1",
"valid_until": "2026-08-23T17:42:30Z",
"links": { "bind": "/rewrites/rwt_6cc4ea05-1505-46dd-bedd-9fccb5f28788/bind" }
} 2. Bind
POST /rewrites/rwt_6cc4ea05-1505-46dd-bedd-9fccb5f28788/bind
Idempotency-Key: 1a2b3c4d-5e6f-4789-901a-bcdef0123456
{}
HTTP/1.1 200 OK
{
"rewrite_id": "rwt_6cc4ea05-1505-46dd-bedd-9fccb5f28788",
"status": "bound",
"transaction_id": "transaction_4d5e6f70-5a0a-4b12-a332-f3142d8f2cde",
"prior_policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100", // status now: rewritten
"successor_policy_id": "pol_c9d3e4f5-3003-4d6e-9c44-3d0c6f9e2345",
"bound_at": "2026-09-15T14:21:08Z"
} Audit: reported-exposure premium reconciliation
End of policy term — payroll (or receipts, etc.) came in higher than
rated. Server computes (reported − rated) × rate_per_unit per
exposure and sums.
1. Quote the audit
POST /audits
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"audit_period_start": "2025-10-01",
"audit_period_end": "2026-09-30",
"exposures": [
{ "basis": "payroll", "rated_amount": 2000000, "reported_amount": 2400000, "rate_per_unit": 0.0085 },
{ "basis": "receipts", "rated_amount": 5000000, "reported_amount": 4800000, "rate_per_unit": 0.012 }
]
}
HTTP/1.1 201 Created
{
"audit_id": "aud_7dd5fb16-1606-477e-cfee-aafdc6038899",
"status": "draft",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"audit_premium": 1000.00, // = (400k×0.0085) + (-200k×0.012) = 3400 - 2400
"exposure_results": [
{ "basis": "payroll", "variance_amount": 3400.00 },
{ "basis": "receipts", "variance_amount": -2400.00 }
],
"valid_until": "2026-10-01T18:00:00Z",
"links": { "bind": "/audits/aud_7dd5fb16-1606-477e-cfee-aafdc6038899/bind" }
} 2. Bind
POST /audits/aud_7dd5fb16-1606-477e-cfee-aafdc6038899/bind
Idempotency-Key: 2c3d4e5f-6a7b-4cde-f012-3456789abcde
{}
HTTP/1.1 200 OK
{
"audit_id": "aud_7dd5fb16-1606-477e-cfee-aafdc6038899",
"status": "bound",
"transaction_id": "transaction_5e6f7081-6b1b-4c23-b443-04253f9143ef",
"premium_change": 1000.00, // additional billable
"new_total_premium": 26000.00,
"bound_at": "2026-10-01T18:04:55Z"
} Extension: stretch the expiration by 60 days
Insured needs more time to renew under a new structure. Server pro-rates the daily premium rate over the extension window and refreshes the MEP for the longer term.
1. Quote the extension
POST /extensions
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"extended_expiration": "2026-11-30" // original expiration was 2026-09-30
}
HTTP/1.1 201 Created
{
"extension_id": "ext_4ae2c8d3-1303-44ee-bbcf-7daa93d06566",
"status": "draft",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"original_expiration": "2026-09-30",
"new_expiration": "2026-11-30",
"additional_premium": 4109.59, // 25,000 × (61 / 365)
"new_mep": 7273.97, // refreshed for the longer term
"valid_until": "2026-09-29T15:00:00Z",
"links": { "bind": "/extensions/ext_4ae2c8d3-1303-44ee-bbcf-7daa93d06566/bind" }
} 2. Bind
POST /extensions/ext_4ae2c8d3-1303-44ee-bbcf-7daa93d06566/bind
Idempotency-Key: 3d4e5f60-7c8d-4def-1023-4567890abcde
{}
HTTP/1.1 200 OK
{
"extension_id": "ext_4ae2c8d3-1303-44ee-bbcf-7daa93d06566",
"status": "bound",
"transaction_id": "transaction_6f708192-7c2c-4d34-c554-15364f0254f0",
"premium_change": 4109.59,
"new_total_premium": 29109.59,
"new_expiration": "2026-11-30",
"bound_at": "2026-09-29T15:04:21Z"
} OOS rebase: insert an endorsement before later transactions
Discovery: an audit reveals the GL limit was supposed to step up back in April, but two endorsements have already applied since. The OOS rebase walks every downstream applied transaction and emits a void → offset → replace plan that you can review before binding.
1. Quote the rebase plan
POST /oos-rebases
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"oos_effective_date": "2026-04-15", // BEFORE seq=4 (2026-05-20) and seq=5 (2026-07-02)
"oos_premium_change": 1500, // additional premium for the missed limit step-up
"description": "Retroactive limit increase — discovered during interim audit"
}
HTTP/1.1 201 Created
{
"rebase_id": "obs_8f0a1b22-1707-4881-d111-c2f3e9215abc",
"status": "draft",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"oos_effective_date": "2026-04-15",
"oos_premium_change": 1500.00,
"rated_result": {
"rows": [
{ "kind": "void", "void_of_seq": 4, "premium_change": -250.00 },
{ "kind": "offset", "for_seq": 4, "premium_change": 250.00 },
{ "kind": "replace", "supersedes": 4, "premium_change": 250.00 },
{ "kind": "void", "void_of_seq": 5, "premium_change": -1850.00 },
{ "kind": "offset", "for_seq": 5, "premium_change": 1850.00 },
{ "kind": "replace", "supersedes": 5, "premium_change": 1850.00 },
{ "kind": "oos", "premium_change": 1500.00 }
],
"totals": { "net_premium_change": 1500.00, "row_count": 7 }
},
"valid_until": "2026-09-30T19:00:00Z",
"links": { "bind": "/oos-rebases/obs_8f0a1b22-1707-4881-d111-c2f3e9215abc/bind" }
} 2. Bind the whole plan
POST /oos-rebases/obs_8f0a1b22-1707-4881-d111-c2f3e9215abc/bind
Idempotency-Key: 4e5f6071-8d9e-4ef0-1234-567890abcdef
{}
HTTP/1.1 200 OK
{
"rebase_id": "obs_8f0a1b22-1707-4881-d111-c2f3e9215abc",
"status": "bound",
"transaction_id": "transaction_7081928a-7d3d-4e45-d665-26475f1364f1",
"applied_rows": 7,
"premium_change": 1500.00,
"bound_at": "2026-09-29T19:04:08Z"
} Cancellation + reinstatement in the downstream set
The rebase engine handles a downstream cancellation/reinstatement chain natively: each cancellation is re-rated against the post-OOS running written premium, the paired reinstatement mirrors the new return amount. The emitted plan looks like:
oos → endorsement at oos_effective_date
offset+replace → for each downstream endorsement (state-neutral)
offset+replace → for the cancellation: offset is state-neutral; replacement
is a re-rated cancellation (status flips to cancelled)
offset+replace → for the reinstatement: offset is state-neutral; replacement
is the paired reinstatement (status flips back to in_force) Reinstatement / non-renewal (deterministic state-flip)
These two transaction kinds don't need a quote step — there's nothing to
price. They go through POST /process-transaction directly
(see the API reference).
Reinstatement (after late-pay cancellation)
POST /process-transaction
Idempotency-Key: 5f607182-9eaf-4f01-2345-67890abcdef1
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"transaction_kind": "reinstatement",
"effective_date": "2026-10-15",
"description": "Reinstatement after payment received"
}
HTTP/1.1 200 OK
{
"transaction_id": "transaction_819203ab-7d4e-4f56-d776-37586f24750f",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"transaction_type": "reinstatement",
"sequence_number": 9,
"premium_change": 0,
"new_total_premium": 29109.59,
"after_state": { "status": "in_force" }
} Non-renewal (formal decline-to-renew)
POST /process-transaction
Idempotency-Key: 60718293-afb0-4012-3456-7890abcdef12
{
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"transaction_kind": "non_renewal",
"effective_date": "2026-08-30", // formal NON-RENEWAL notice date
"description": "Loss-ratio adverse; non-renewing per uw"
}
HTTP/1.1 200 OK
{
"transaction_id": "transaction_92031bcd-7e5f-4067-d887-48697f35861f",
"policy_id": "pol_3a8f1c20-0001-4d6e-9c22-1b8a4f7f9100",
"transaction_type": "non_renewal",
"premium_change": 0,
"after_state": { "status": "pending_non_renew" }
} Auth + signing reminder
Every request above carries the same signature headers that the Authentication page covers in detail. We've omitted them in the bodies above for readability; the full header set on every signed call is:
X-API-Key: odn_sb_<your_api_key>
X-Timestamp: <unix seconds>
X-Signature: sha256=<hex hmac-sha256 of "<ts>.<rawBody>">
Idempotency-Key: <fresh uuid v4 — required on every bind step>
Content-Type: application/json
Skadi-API-Version: 2026-04-25 Skadi-API-Version is an optional pin — omit it and the key's
default version applies. The same header set applies whatever client you
build it in (curl, Node, Python).
Variance
Partner API keys never see the charged override object
(amount, reason_cd) — system-rated premium is
authoritative. Variance is internal-only
(write:transactions:override scope).