Skadi Partner API Console

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:

ResourceDraft TTL
endorsement, cancellation, extension, audit, oos-rebase24 hours
renewal, rewrite7 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).