{"openapi":"3.0.3","info":{"title":"SecondAppraisal GAP Provider API","version":"1.1.0","description":"Referral submission and status tracking for GAP administrators, lenders and carriers, and the Garage Hub Membership roster.\n\n- Every request body is JSON (`Content-Type: application/json`). A field the schema does not name is refused with 400 `unknown_field`. A field a referral must never carry (an SSN, a birth date, an account number) is refused first, with 400 `prohibited_field`, naming the field and never its value.\n- Money is integer cents. Timestamps are ISO 8601 in UTC.\n- Every refusal the API makes is an RFC 9457 problem (`application/problem+json`): a stable `code`, the `error` sentence, `field_errors` on validation, and the `request_id` that is also sent as `X-Request-Id`. `x-error-catalog` lists every code.\n- During a failover, the platform can answer a write (POST or PATCH) itself, before the API sees it: 503 with `Retry-After: 60` and a plain JSON body, `\"error\": \"platform_standby\"`, with no problem members and no `X-Request-Id`. Nothing was written: send the same request again after `Retry-After` seconds, with the same `Idempotency-Key` where the operation takes one. Reads are not refused this way.\n- Responses are open: ignore fields you don't know.\n- A later release may add values to an enum a response carries; each such enum says what to do with a value you don't recognise.\n- Each operation states the key permission it needs (`x-required-permission`), its rate class (`x-rate-class`), its idempotency (`x-idempotency`), which key modes reach it (`x-availability`) and the most sensitive data class it carries (`x-data-class`, described in `x-data-classes`). A `staged` operation answers 404 `operation_not_open` until the switch `x-availability.opens_with` names opens.\n- A field marked `x-requires-permission` is null for a key that doesn't hold the permission it names, wherever the field appears in a response. A legacy key (`sa_gap_`) gets every such field its scopes reach.\n- Pipeline state is exposed through the provider-facing milestone timeline only."},"servers":[{"url":"https://secondappraisal.com"}],"security":[{"bearerAuth":[]},{"signedRequest":[]}],"tags":[{"name":"referrals","description":"Refer a borrower, follow the referral, edit or cancel it."},{"name":"analytics","description":"Your funnel, outcomes and spend."},{"name":"plans","description":"The Garage Hub Membership: the vehicles you enroll, the consultations drawn against them and your monthly statements. The membership is not insurance: nothing is paid to a member and no result is promised."},{"name":"events","description":"What happened to your referrals and enrollments, for polling instead of, or beside, webhooks."},{"name":"document","description":"This document."},{"name":"credentials","description":"The key making the call: what it is, what it may do and when it expires; and a signed credential's activation."}],"paths":{"/api/gap/v1/referrals":{"post":{"operationId":"createReferral","tags":["referrals"],"summary":"Create a referral","description":"Submits one borrower referral. Requires an executed MSA; provider-paid and split-pay referrals also need a billing method. Send an `Idempotency-Key` header to make retries safe: a replay returns the original referral with `idempotent_replay: true` and 200. Keys are per institution and per key mode: the same key sent with a live key and a test key makes two referrals, and a live key can't send a key that starts with `test:` (409 `idempotency_key_mode_conflict`). A key that doesn't hold `referrals:read` gets an acknowledgement instead of the referral: its `id`, `object` and `status`, never its data. Such a key's replay must repeat its own request exactly: a key that came with a different request, or with one sent before requests were fingerprinted, is 422 `idempotency_key_reused`. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Makes a retry safe: a request with a key your institution already used returns the original result with `idempotent_replay: true`. At most 255 characters, trimmed; a longer key is refused, never cut. On a signed request (`signedRequest`) it is required and signed: 1 to 255 visible ASCII characters (`!` to `~`), no spaces, or the call gets 401 `signature_profile_invalid`.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`). A field a referral must never carry is refused before anything else is checked (`prohibited_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralCreateInput"}}}},"responses":{"200":{"description":"An idempotent replay: the original referral, whatever this body says. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralCreateResult"},{"$ref":"#/components/schemas/ReferralCreateAcknowledgement"}]}}}},"201":{"description":"Created. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralCreateResult"},{"$ref":"#/components/schemas/ReferralCreateAcknowledgement"}]}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `prohibited_field`, `validation_failed`, `idempotency_key_too_long`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","prohibited_field","validation_failed","idempotency_key_too_long"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`, `provider_suspended`, `provider_not_active`, `msa_required`, `billing_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable","provider_suspended","provider_not_active","msa_required","billing_required"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `script_version_stale`, `duplicate_reference`, `idempotency_key_mode_conflict`, `duplicate_referral`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","properties":{"existing_referral_id":{"x-data-class":"institution_confidential","type":"string","description":"On `duplicate_referral`: your referral for the same loss. Never sent to a key that doesn't hold `referrals:read`."}}}]}}},"x-error-codes":["signature_replay","script_version_stale","duplicate_reference","idempotency_key_mode_conflict","duplicate_referral"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"422":{"description":"Refused. `code` is one of: `email_required_sms_disabled`, `idempotency_key_reused`, `state_not_served`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","properties":{"state":{"x-data-class":"borrower_personal","type":"string","description":"On `state_not_served`: the state judged, a two-letter code."},"basis":{"x-data-class":"public","type":"string","enum":["garaged","loss"],"description":"On `state_not_served`: `garaged` when the state judged is `garaged_state`, `loss` when it is `loss_state`. New values may be added: treat one you don't recognise as unknown, and don't fail."}}}]}}},"x-error-codes":["email_required_sms_disabled","idempotency_key_reused","state_not_served"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:write","x-rate-class":"write","x-idempotency":{"kind":"header","header":"Idempotency-Key","maxLength":255},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"borrower_contact"},"get":{"operationId":"listReferrals","tags":["referrals"],"summary":"List referrals","description":"Your referrals, newest first, a page at a time. Once a borrower withdraws permission to report a referral's progress to you, the referral shows `status: reporting_withdrawn` and `reporting_withdrawn_at`, the fields you supplied, and no progress. `status` matches the status shown: `reporting_withdrawn` lists those referrals, and any other status leaves them out. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"status","in":"query","description":"Comma-separated statuses, e.g. `invited,activated`. Case-insensitive. `?status=` is no filter; a value naming no status (`,`, or only spaces) is refused. A status matches the status a referral is shown with: `reporting_withdrawn` lists the referrals whose borrower withdrew permission to report their progress, and any other status leaves them out.","style":"form","explode":false,"schema":{"x-data-class":"public","items":{"type":"string","enum":["submitted","invited","handoff_pending","outreach_queued","contact_attempted","activated","in_progress","settled","closed","declined","unreachable","expired","cancelled","reporting_withdrawn"]},"type":"array"}},{"name":"external_ref","in":"query","description":"Exact match on the external_ref you sent, trimmed. `?external_ref=` is no filter; only spaces is refused. Deprecated: the reference travels in the URL, where the systems it passes through can record it. It still filters; retrieve a referral by the id its create returned instead.","deprecated":true,"schema":{"x-data-class":"institution_confidential","type":"string"}},{"name":"limit","in":"query","description":"Page size, 1 to 100, written in digits. A blank value means the default.","schema":{"x-data-class":"public","type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"starting_after","in":"query","description":"Cursor: the previous page's `next_cursor` (its last referral's id, or with `updated_after` an opaque `u1.` cursor). `?starting_after=` is the first page; only spaces is refused.","schema":{"x-data-class":"institution_confidential","type":"string"}},{"name":"updated_after","in":"query","description":"Only the referrals changed after this time (ISO 8601 with its offset, such as `2026-10-01T00:00:00Z`, from 0001-01-01T00:00:00Z through 9999-12-31T23:59:59.999Z), oldest change first; a referral that changes again before you reach it moves to a later page. A referral whose borrower withdraws permission to report its progress is listed once more, at `reporting_withdrawn_at`, and never again. A change is listed about a minute after it happens. Every page, the last and an empty one too, returns an opaque `u1.` `next_cursor`: to poll, keep the latest one and send it as `starting_after` with the same `updated_after`, never a time from your own clock, which misses the changes made in the minute before each poll. A cursor works only for the institution and mode it was issued to; if one is refused (400 `invalid_cursor`), start again from your `updated_after` without it. `?updated_after=` is no filter.","schema":{"x-data-class":"public","type":"string","format":"date-time","example":"2026-10-01T00:00:00Z"}}],"responses":{"200":{"description":"A newest-first page.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralList"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`, `invalid_cursor`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed","invalid_cursor"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"borrower_contact"}},"/api/gap/v1/referrals/{id}":{"get":{"operationId":"getReferral","tags":["referrals"],"summary":"Retrieve a referral","description":"The full referral, with the provider-facing milestone timeline and the financial panel. Once a borrower withdraws permission to report a referral's progress to you, the referral shows `status: reporting_withdrawn` and `reporting_withdrawn_at`, the fields you supplied, and no progress. Its `timeline` and `financial` are then null, as are `consultation_number` and `days_in_negotiation`. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"id","in":"path","description":"The referral's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}}],"responses":{"200":{"description":"The referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralDetail"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable"]},"404":{"description":"Refused. `code` is one of: `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"borrower_contact"},"patch":{"operationId":"updateReferral","tags":["referrals"],"summary":"Update or cancel a referral","description":"`loan_payoff_cents` and `deductible_cents` are editable any time. Every other field, and the program terms, until the borrower activates (409 after). `{ \"action\": \"cancel\" }`, sent on its own, cancels before activation (409 after). A referral whose borrower withdrew permission to report its progress is 409 `reporting_withdrawn`, whatever the request asks, and nothing is written. A key that doesn't hold `referrals:read` gets an acknowledgement instead of the referral: its `id`, `object` and `status`, never its data. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"id","in":"path","description":"The referral's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`). A field a referral must never carry is refused before anything else is checked (`prohibited_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralPatchInput"}}}},"responses":{"200":{"description":"The updated referral. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralDetail"},{"$ref":"#/components/schemas/ReferralAcknowledgement"}]}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `prohibited_field`, `validation_failed`, `nothing_to_update`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","prohibited_field","validation_failed","nothing_to_update"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`, `provider_suspended`, `provider_not_active`, `billing_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable","provider_suspended","provider_not_active","billing_required"]},"404":{"description":"Refused. `code` is one of: `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `referral_locked`, `cancel_not_allowed`, `economics_locked`, `reporting_withdrawn`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","referral_locked","cancel_not_allowed","economics_locked","reporting_withdrawn"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"422":{"description":"Refused. `code` is one of: `state_not_served`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","properties":{"state":{"x-data-class":"borrower_personal","type":"string","description":"On `state_not_served`: the state judged, a two-letter code."},"basis":{"x-data-class":"public","type":"string","enum":["garaged","loss"],"description":"On `state_not_served`: `garaged` when the state judged is `garaged_state`, `loss` when it is `loss_state`. New values may be added: treat one you don't recognise as unknown, and don't fail."}}}]}}},"x-error-codes":["state_not_served"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:write","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"borrower_contact"}},"/api/gap/v1/referrals/bulk":{"post":{"operationId":"bulkCreateReferrals","tags":["referrals"],"summary":"Create referrals in bulk","description":"Up to 500 referrals in one call. Each row is validated and created on its own: valid rows are created even when siblings fail, and `results` reports every row. A row may carry its own `idempotency_key` and, for a warm handoff, its `disclosure`. An institution without an executed MSA gets 403 `msa_required` for the whole call; a provider-paid or split-pay row without a billing method fails with the row code `billing_required`. A key that doesn't hold `referrals:read` gets each row's referral as an acknowledgement (its `id`, `object` and `status`), and its rows replay only their own requests, as a single create's do. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it: each row's `idempotency_key` is what makes a retry safe.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 1,048,576 bytes. A field the schema does not name is refused (`unknown_field`). A field a referral must never carry is refused before anything else is checked (`prohibited_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralBulkInput"}}}},"responses":{"200":{"description":"Row-level results. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralBulkResult"},{"$ref":"#/components/schemas/ReferralBulkAcknowledgement"}]}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `prohibited_field`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","prohibited_field","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`, `provider_suspended`, `provider_not_active`, `msa_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable","provider_suspended","provider_not_active","msa_required"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`, `too_many_rows`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large","too_many_rows"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:write","x-rate-class":"bulk","x-idempotency":{"kind":"row","field":"idempotency_key","maxLength":255},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"borrower_contact"}},"/api/gap/v1/referrals/{id}/simulate":{"post":{"operationId":"simulateReferral","tags":["referrals"],"summary":"Simulate a sandbox referral's next step","description":"Test keys only. Moves one of your sandbox referrals one step along its lifecycle, and sends the webhook event a live referral sends at that step (`referral.invited`, `referral.activated`, `referral.declined` or `settlement.updated`) to your test endpoints, marked `livemode: false`. Nothing reaches a borrower and nothing is billed. A key that doesn't hold `referrals:read` gets an acknowledgement instead of the referral: its `id`, `object` and `status`, never its data. A live key gets 403 `simulate_requires_test_key`, an id that is not one of your sandbox referrals 404, and a step off the lifecycle 409 `invalid_transition`. A referral whose borrower withdrew permission to report its progress is 409 `reporting_withdrawn`, whatever the request asks, and nothing is written. Staged: it opens with the referral sandbox, and until then every call answers 404 `operation_not_open`.","parameters":[{"name":"id","in":"path","description":"The referral's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralSimulateInput"}}}},"responses":{"200":{"description":"The sandbox referral after the step. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralDetail"},{"$ref":"#/components/schemas/ReferralAcknowledgement"}]}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`, `provider_suspended`, `provider_not_active`, `simulate_requires_test_key`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable","provider_suspended","provider_not_active","simulate_requires_test_key"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `invalid_transition`, `reporting_withdrawn`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","invalid_transition","reporting_withdrawn"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:write","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["test"],"opens_with":"referral_sandbox"},"x-data-class":"borrower_contact"}},"/api/gap/v1/referrals/{id}/attest":{"post":{"operationId":"attestReferral","tags":["referrals"],"summary":"Attest a warm handoff","description":"For a warm-handoff referral created without its `disclosure`: you certify that your staff delivered the disclosure script and that the borrower agreed to be contacted, the same attestation as create's `disclosure`, and the referral is queued for our outreach. Accepted only while the referral waits for its attestation: after that, or for an invitation referral, it is a 409 `invalid_transition`. A script version that is not the current script is a 409 `script_version_stale`. A referral whose borrower withdrew permission to report its progress is 409 `reporting_withdrawn`, whatever the request asks, and nothing is written. A test key attests its sandbox referrals, which reach no one: no outreach is opened for them. A key that doesn't hold `referrals:read` gets an acknowledgement instead of the referral: its `id`, `object` and `status`, never its data. Staged: it opens with the referral sandbox, and until then every call answers 404 `operation_not_open`.","parameters":[{"name":"id","in":"path","description":"The referral's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`). A field a referral must never carry is refused before anything else is checked (`prohibited_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisclosureInput"}}}},"responses":{"200":{"description":"The referral, queued for our outreach. For a key that doesn't hold `referrals:read`: an acknowledgement, never the referral.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralDetail"},{"$ref":"#/components/schemas/ReferralAcknowledgement"}]}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `prohibited_field`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","prohibited_field","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`, `provider_suspended`, `provider_not_active`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable","provider_suspended","provider_not_active"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `script_version_stale`, `invalid_transition`, `reporting_withdrawn`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","script_version_stale","invalid_transition","reporting_withdrawn"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:write","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["live","test"],"opens_with":"referral_sandbox"},"x-data-class":"borrower_contact"}},"/api/gap/v1/analytics":{"get":{"operationId":"getAnalytics","tags":["analytics"],"summary":"Savings and spend analytics","description":"Your referral funnel and activation rate, settled outcomes (uplift, exposure reduction), spend (fees, ROI multiple, net savings), and breakdowns by market and carrier: the numbers the portal's Analytics page shows. Filter on the referral's creation date with `from` (inclusive) and `to` (exclusive). Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them.","parameters":[{"name":"from","in":"query","description":"Referrals created on or after this date (UTC), YYYY-MM-DD. `?from=` is no bound. Spaces around it are ignored.","schema":{"x-data-class":"public","type":"string","format":"date","example":"2026-01-01"}},{"name":"to","in":"query","description":"Referrals created before this date (UTC), YYYY-MM-DD: the bound is exclusive. Must be after `from`. `?to=` is no bound. Spaces around it are ignored.","schema":{"x-data-class":"public","type":"string","format":"date","example":"2026-07-01"}}],"responses":{"200":{"description":"The report.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsReport"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"reporting:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live"]},"x-data-class":"institution_confidential"}},"/api/gap/v1/plans/enrollments":{"post":{"operationId":"createPlanEnrollment","tags":["plans"],"summary":"Enroll one vehicle","description":"Idempotent per institution and key mode with the `Idempotency-Key` header: a replay returns the original with `idempotent_replay: true` and 200; a key already used in the other mode or for another VIN is a 422 `idempotency_key_reused`. Partner-billed institutions get an ACTIVE enrollment at once (you are the payer of record); direct-collect institutions get a PENDING one with an `activation_url` for the member.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Makes a retry safe: a request with a key your institution already used returns the original result with `idempotent_replay: true`. At most 255 characters, trimmed; a longer key is refused, never cut. On a signed request (`signedRequest`) it is required and signed: 1 to 255 visible ASCII characters (`!` to `~`), no spaces, or the call gets 401 `signature_profile_invalid`.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentInput"}}}},"responses":{"200":{"description":"An idempotent replay: the original enrollment.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentCreateResult"}}}},"201":{"description":"Enrolled.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentCreateResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`, `idempotency_key_too_long`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed","idempotency_key_too_long"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`, `membership_not_enabled`, `membership_rider_unsigned`, `membership_billing_mode_required`, `membership_billing_method_required`, `plan_not_enabled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required","plan_not_enabled"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `vin_already_live`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","vin_already_live"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"422":{"description":"Refused. `code` is one of: `vin_already_consulted`, `state_required`, `state_blocked`, `vin_invalid`, `invalid_customer_price`, `idempotency_key_reused`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["vin_already_consulted","state_required","state_blocked","vin_invalid","invalid_customer_price","idempotency_key_reused"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:write","x-rate-class":"write","x-idempotency":{"kind":"header","header":"Idempotency-Key","maxLength":255},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"credential"},"get":{"operationId":"listPlanEnrollments","tags":["plans"],"summary":"List your roster","description":"Your enrollments in this key's mode, newest first. A key scoped to `read` may list, and an institution whose membership gate is closed can still read its roster.","parameters":[{"name":"vin","in":"query","description":"Only this VIN: 17 characters; case, spaces, dashes and dots are ignored. The check digit is not checked. `?vin=` is no filter. Deprecated: the VIN travels in the URL, where the systems it passes through can record it. It still filters; retrieve an enrollment by the id its create returned instead.","deprecated":true,"schema":{"x-data-class":"borrower_personal","type":"string"}},{"name":"status","in":"query","description":"Only this status, case-insensitive. `?status=` is no filter.","schema":{"x-data-class":"public","type":"string","enum":["PENDING","ACTIVE","PAST_DUE","LAPSED","CANCELLED"]}},{"name":"cursor","in":"query","description":"The previous page's `next_cursor`. `?cursor=` is the first page; only spaces is refused.","schema":{"x-data-class":"institution_confidential","type":"string"}},{"name":"limit","in":"query","description":"Page size, 1 to 200, written in digits. A blank value means the default.","schema":{"x-data-class":"public","type":"integer","minimum":1,"maximum":200,"default":100}}],"responses":{"200":{"description":"A newest-first page.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentList"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"credential"}},"/api/gap/v1/plans/enrollments/{id}":{"get":{"operationId":"getPlanEnrollment","tags":["plans"],"summary":"Retrieve one enrollment","description":"One of your enrollments in this key's mode.","parameters":[{"name":"id","in":"path","description":"The enrollment's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}}],"responses":{"200":{"description":"The enrollment.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollment"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"credential"},"patch":{"operationId":"updatePlanEnrollment","tags":["plans"],"summary":"Cancel, swap the VIN, or edit contact details","description":"`{ \"cancel\": true }`, on its own, cancels: at once for a partner-billed enrollment, at period end for a direct-collect one. `{ \"new_vin\": … }` enrolls the new VIN and closes this enrollment (201): the new vehicle's eligibility window starts again; `garaged_state`, `external_ref` and each member field carry over unless sent, and a paying direct-collect member's subscription moves to the new enrollment. Any other body edits `external_ref` and `member` only; a body with nothing to apply is a 400. In external_ref, send your own enrollment or file ID. Never a loan, account or policy number (a credit union member number is an account number).","parameters":[{"name":"id","in":"path","description":"The enrollment's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentPatchInput"}}}},"responses":{"200":{"description":"The cancelled or edited enrollment.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollment"}}}},"201":{"description":"Swapped: the new enrollment, naming the one it replaced.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentSwapResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`, `nothing_to_update`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed","nothing_to_update"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`, `membership_not_enabled`, `membership_rider_unsigned`, `membership_billing_mode_required`, `membership_billing_method_required`, `plan_not_enabled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required","plan_not_enabled"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `not_live`, `vin_already_live`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","not_live","vin_already_live"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"422":{"description":"Refused. `code` is one of: `vin_already_consulted`, `state_required`, `state_blocked`, `vin_invalid`, `invalid_customer_price`, `idempotency_key_reused`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["vin_already_consulted","state_required","state_blocked","vin_invalid","invalid_customer_price","idempotency_key_reused"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:write","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"credential"}},"/api/gap/v1/plans/enrollments/bulk":{"post":{"operationId":"bulkCreatePlanEnrollments","tags":["plans"],"summary":"Enroll up to 500 vehicles","description":"Each row is validated and enrolled on its own; `results` reports every row. A row's `idempotency_key` replays it like the single endpoint's header. An `Idempotency-Key` header (at most 251 characters) keys every row without its own as `<header>:<index>`, so re-sending the same batch replays it.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A batch key: every row without its own `idempotency_key` is keyed `<header>:<index>`, so re-sending the same batch replays it. At most 251 characters, trimmed. On a signed request (`signedRequest`) it is required and signed: 1 to 255 visible ASCII characters (`!` to `~`), no spaces, or the call gets 401 `signature_profile_invalid`.","schema":{"type":"string","maxLength":251}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 1,048,576 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentBulkInput"}}}},"responses":{"200":{"description":"Row-level results.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanEnrollmentBulkResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`, `idempotency_key_too_long`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed","idempotency_key_too_long"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`, `membership_not_enabled`, `membership_rider_unsigned`, `membership_billing_mode_required`, `membership_billing_method_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`, `too_many_rows`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large","too_many_rows"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:write","x-rate-class":"bulk","x-idempotency":{"kind":"header+row","header":"Idempotency-Key","maxLength":251,"field":"idempotency_key","rowMaxLength":255},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"borrower_contact"}},"/api/gap/v1/plans/roster-sync":{"post":{"operationId":"syncPlanRoster","tags":["plans"],"summary":"Reconcile your full roster","description":"Every VIN in the list ends up live; every live VIN of yours not in the list is cancelled (you stopped billing that member), at period end for a paying direct-collect member. Only an explicit `{ \"enrollments\": [] }` empties the roster; a bare array is refused. Any malformed row refuses the whole sync (400 with `rejected`), so a parse error can never cancel a live member. Built for a nightly job from a loan-servicing system. In external_ref, send your own enrollment or file ID. Never a loan, account or policy number (a credit union member number is an account number).","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 1,048,576 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRosterSyncInput"}}}},"responses":{"200":{"description":"What the sync did.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRosterSyncResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","properties":{"rejected":{"type":"array","items":{"$ref":"#/components/schemas/PlanRosterRejection"},"description":"On `validation_failed` for rows: every row that failed, by index, with its field errors. Nothing was changed."}}}]}}},"x-error-codes":["invalid_json","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`, `membership_not_enabled`, `membership_rider_unsigned`, `membership_billing_mode_required`, `membership_billing_method_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`, `too_many_rows`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large","too_many_rows"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:write","x-rate-class":"bulk","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"borrower_contact"}},"/api/gap/v1/plans/loss-notices":{"post":{"operationId":"recordPlanLossNotice","tags":["plans"],"summary":"Tell us a member vehicle was declared a total loss","description":"The payoff-request trigger: you usually know before the member does. Records the notice on the enrollment so we can reach the member with a running start. 404 `not_a_member_vehicle` when the VIN is not a live member vehicle of yours; a `date_of_loss` more than a day ahead is a 400.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanLossNoticeInput"}}}},"responses":{"200":{"description":"Recorded.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanLossNoticeResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`, `vin_invalid`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed","vin_invalid"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`, `membership_not_enabled`, `membership_rider_unsigned`, `membership_billing_mode_required`, `membership_billing_method_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`, `not_a_member_vehicle`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed","not_a_member_vehicle"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:write","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"borrower_personal"}},"/api/gap/v1/plans/redemptions":{"get":{"operationId":"listPlanRedemptions","tags":["plans"],"summary":"Member consultations drawn against your roster","description":"Ids, VIN, status, dates and the outcome once settled: never the consultation's internals. Newest first, a page at a time. A redemption names the referral its member consultation made (`referral_id`); once the borrower withdraws permission to report that referral's progress to you, the redemption shows `status: REPORTING_WITHDRAWN` and `reporting_withdrawn_at`, with no outcome and no `settled_at`, as the referral itself reads (`status: reporting_withdrawn`).","parameters":[{"name":"cursor","in":"query","description":"The previous page's `next_cursor`. `?cursor=` is the first page; only spaces is refused.","schema":{"x-data-class":"institution_confidential","type":"string"}},{"name":"limit","in":"query","description":"Page size, 1 to 200, written in digits. A blank value means the default.","schema":{"x-data-class":"public","type":"integer","minimum":1,"maximum":200,"default":200}}],"responses":{"200":{"description":"A newest-first page.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRedemptionList"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"borrower_personal"}},"/api/gap/v1/plans/statements":{"get":{"operationId":"listPlanStatements","tags":["plans"],"summary":"Your monthly membership statements","description":"Your last 36 monthly statements, newest first. A test key gets an empty list: statements bill only live enrollments.","responses":{"200":{"description":"Your statements.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanStatementList"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed"]},"404":{"description":"Refused. `code` is one of: `membership_program_closed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["membership_program_closed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `auth_unavailable`, `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["auth_unavailable","internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"plans:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"institution_confidential"}},"/api/gap/v1/events":{"get":{"operationId":"listEvents","tags":["events"],"summary":"List your events","description":"Your institution's events from the last 30 days, oldest first: every event we record for you, whether or not a webhook endpoint takes it, as a thin event (ids, your `external_ref`, statuses, the milestone reached, outcome codes and timestamps; never a borrower's contact details, a payoff, a VIN or an amount). Each event type needs its own permission: the referral and review events `referrals:read`, `charge.created` `reporting:read`, and the membership plan events `plans:read`. A key lists only the types it holds the permission for, and a key that holds none of them gets an empty list; a key without `referrals:read` gets each event's referral as its `id` only. The membership plan events are listed only while the membership program is open to your institution, and a test key lists the referral and charge events only once the referral sandbox opens, as those operations answer. The review events (`review.completed`, `review.updated` and `referral.closed`) are listed only while review records are open, as the review-status lookup and the review record answer. Once a borrower withdraws permission to report a referral's progress, its events from before the withdrawal stay listed, and from the withdrawal on, only the event that announces it is listed (`referral.closed`, or `review.updated` citing the `REPORTING_WITHDRAWN` version); its `charge.created` and the membership plan's enrollment and statement events stay, while `plan.benefit.redeemed` and `plan.benefit.completed` follow the referral the redemption made. A live key lists live events, and a test key sandbox ones. An event is listed about a minute after it happens. To poll, keep the `next_cursor` every page returns and send it as `starting_after`, never an event's `id`: an event can reach the feed after a newer one's webhook has gone out, so a position taken from a webhook would skip it. Webhooks and the feed carry the same event ids, so drop duplicates by `id` across both. Which types are listed is decided when you read: the events of a surface closed then are not listed later to a poller that has read past them. To see them, or after a 400 `invalid_cursor` (a cursor works only for the institution and mode it was issued to), start again without `starting_after`: the feed lists the last 30 days, and you drop the ids you have. Staged: it opens with the partner security platform, and until then every call answers 404 `operation_not_open`.","parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100, written in digits. A blank value means the default.","schema":{"x-data-class":"public","type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"starting_after","in":"query","description":"The previous page's `next_cursor`: an opaque position, the last event this feed listed to you; the page starts after it. Never an event's `id`, not even one a webhook delivered: an event can reach the feed after a newer one's webhook has gone out, so a position taken from a webhook would skip it. A cursor works only for the institution and mode it was issued to; anything else is 400 `invalid_cursor`. `?starting_after=` is the first page; only spaces is refused.","schema":{"x-data-class":"institution_confidential","type":"string"}}],"responses":{"200":{"description":"An oldest-first page of thin events.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventFeedPage"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`, `invalid_cursor`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed","invalid_cursor"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `provider_terminated`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["provider_terminated","edge_auth_required","ip_not_allowed"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"none","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["live","test"],"opens_with":"partner_events"},"x-data-class":"institution_confidential"}},"/api/gap/v1/openapi":{"get":{"operationId":"getOpenApiDocument","tags":["document"],"summary":"This OpenAPI document","description":"The OpenAPI 3.1 document for this API, generated from the contract the API runs on. No key needed. `?oas=3.0` serves a derived OpenAPI 3.0.3 copy.","security":[],"parameters":[{"name":"oas","in":"query","description":"`3.0` serves the derived OpenAPI 3.0.3 copy, for tooling that predates 3.1.","schema":{"x-data-class":"public","type":"string","enum":["3.1","3.0"],"default":"3.1"}}],"responses":{"200":{"description":"The document.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"403":{"description":"Refused. `code` is one of: `edge_auth_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["edge_auth_required"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]}},"x-required-permission":"none","x-rate-class":"none","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":[]},"x-data-class":"public"}},"/api/gap/v1/openapi.json":{"get":{"operationId":"getOpenApiDocumentJson","tags":["document"],"summary":"This OpenAPI document (`.json` alias)","description":"The same document as `GET /api/gap/v1/openapi`, under a path ending in `.json` for tools that expect one.","security":[],"parameters":[{"name":"oas","in":"query","description":"`3.0` serves the derived OpenAPI 3.0.3 copy, for tooling that predates 3.1.","schema":{"x-data-class":"public","type":"string","enum":["3.1","3.0"],"default":"3.1"}}],"responses":{"200":{"description":"The document.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"403":{"description":"Refused. `code` is one of: `edge_auth_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["edge_auth_required"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]}},"x-required-permission":"none","x-rate-class":"none","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":[]},"x-data-class":"public","x-alias-of":"getOpenApiDocument"}},"/api/gap/v1/me":{"get":{"operationId":"getMe","tags":["credentials"],"summary":"The key making this call","description":"Which API key made the call and what it may do: its id and prefix, live or test, its format and how it authenticates, the permissions it holds (a legacy key's scopes as the permissions they reach), when it expires, your institution's id and status, and how many address ranges its IP allowlist holds. Any key reaches it, live or test, whatever it may do, so it is the call to make first when a key is refused elsewhere. A terminated institution's key gets 403 `provider_terminated`.","responses":{"200":{"description":"The key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyIntrospection"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `provider_terminated`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["provider_terminated","edge_auth_required","ip_not_allowed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"none","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"available","modes":["live","test"]},"x-data-class":"institution_confidential"}},"/api/gap/v1/credentials/{id}/verify":{"post":{"operationId":"verifyCredential","tags":["credentials"],"summary":"Activate a signed credential","description":"A signed credential registered in the portal starts unactivated: until this call succeeds, every other call it signs gets 401 `credential_not_activated`. Sign this call with the credential itself, `{id}` being its id (the `keyid` it signs with), with an empty body: the signature proves you hold the private key of the public key you registered. It answers the credential with `activated_at`, and a later call answers the same time. A public key is active for one credential at most: if another credential has already activated it, this call gets 409 `public_key_in_use`, and you register a new key pair instead. Any other caller, a bearer key or another credential, gets 404. Staged: it opens when signed credentials open, and until then every call answers 404 `operation_not_open`.","security":[{"signedRequest":[]}],"parameters":[{"name":"id","in":"path","description":"The signed credential's id: the `keyid` its signatures name.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"Idempotency-Key","in":"header","required":true,"description":"On a signed request (`signedRequest`), the only kind this call takes, it is required and signed: 1 to 255 visible ASCII characters (`!` to `~`), no spaces, or the call gets 401 `signature_profile_invalid`. This call keeps no record of it: it is idempotent itself, and a retry is signed again, with a new nonce.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[!-~]+$"}}],"responses":{"200":{"description":"The credential, activated.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialActivation"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `provider_terminated`, `edge_auth_required`, `ip_not_allowed`, `provider_suspended`, `provider_not_active`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["provider_terminated","edge_auth_required","ip_not_allowed","provider_suspended","provider_not_active"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`, `public_key_in_use`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay","public_key_in_use"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"none","x-rate-class":"write","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["live","test"],"opens_with":"signed_credentials"},"x-data-class":"institution_confidential"}},"/api/gap/v1/signature-check":{"post":{"operationId":"checkSignature","tags":["credentials"],"summary":"Check a signature (test credentials)","description":"Planned. For a test credential while you build your signer: send a signed request here and it answers whether the signature verified, with the signature base built from the request as received, to compare byte for byte with yours, and the code the same request would be refused with elsewhere. Test credentials only. Not served yet.","security":[{"signedRequest":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"On a signed request (`signedRequest`), the only kind this call takes, it is required and signed: 1 to 255 visible ASCII characters (`!` to `~`), no spaces, or the call gets 401 `signature_profile_invalid`. This call keeps no record of it: it is idempotent itself, and a retry is signed again, with a new nonce.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[!-~]+$"}}],"responses":{"200":{"description":"What the signature looked like to the API.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatureCheckResult"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `provider_terminated`, `edge_auth_required`, `ip_not_allowed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["provider_terminated","edge_auth_required","ip_not_allowed"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"none","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"planned","modes":["test"]},"x-data-class":"institution_confidential"}},"/api/gap/v1/review-status/lookup":{"post":{"operationId":"lookupReviewStatus","tags":["referrals"],"summary":"Look up review status by VIN, claim number or reference","description":"Finds your referrals by one identifier, sent in the body so it never travels in a URL: `vin`, `claim_number` or `external_ref`, as you sent it on the referral. Each match comes with the latest version of its review record, cited as `{ id, version, hash }` with its status and when it was written, and with its close reason: what the review webhooks carry. Read the version itself with `GET /api/gap/v1/referrals/{id}/review-record`. Newest first, at most 100 (`has_more` says more matched); no match is an empty list. `record` is null while a referral's record has no version to show: before its first, and from the borrower's request that we stop reporting the referral's progress until the version that records it. It writes nothing, but during a failover the platform can still answer it as it answers a write (503 `platform_standby`). Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them. Staged: it opens with review records, once their sweep is on and has finished its first pass; until then every call answers 404 `operation_not_open`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"On a signed request (`signedRequest`) it is required and signed, and must be 1 to 255 visible ASCII characters (`!` to `~`), with no spaces, or the call gets 401 `signature_profile_invalid`. A call with an API key may leave it out or send any value. This operation keeps no record of it, so it makes nothing idempotent here.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"`Content-Type: application/json`, at most 65,536 bytes. A field the schema does not name is refused (`unknown_field`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewStatusLookupInput"}}}},"responses":{"200":{"description":"Your matching referrals, newest first, each with its review status.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewStatusList"}}}},"400":{"description":"Refused. `code` is one of: `invalid_json`, `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["invalid_json","validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"413":{"description":"Refused. `code` is one of: `payload_too_large`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["payload_too_large"]},"415":{"description":"Refused. `code` is one of: `unsupported_media_type`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["unsupported_media_type"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["live","test"],"opens_with":"review_record"},"x-data-class":"borrower_personal"}},"/api/gap/v1/referrals/{id}/review-record":{"get":{"operationId":"getReferralReviewRecord","tags":["referrals"],"summary":"Retrieve a referral's review record","description":"One version of the referral's review record: the latest, or the one `?version=` names. A version is written each time the record changes and never edited, and its `hash` is the SHA-256 of `bundle`'s canonical JSON (RFC 8785), so the version you read later still matches the hash a webhook or the lookup cited. `bundle` holds the record: its status, the verdict once it reached the borrower and whether we can help (never a dollar figure), the review window, outreach, a refusal, a review done elsewhere, the appraisal right, the costs by component and payer, the requirement, the outcome and the copy versions. A redacted version keeps its id, version, hash and status, with `bundle: null`. Once the borrower asks us to stop reporting the referral's progress, the latest version says only that, and the versions before it stay readable by number; until the version that records it is written, there is no latest to show. 404 `not_found` for a referral that isn't yours in this key's mode, a version it doesn't have, or a record with no version to show yet. `costs.engagement` and `costs.institution_charges` are null unless the key also holds `reporting:read`. The `hash` covers the whole record, costs included, so a copy with them null still cites the version but doesn't hash to it. Test keys (`sa_gap_test_...` or `sa_test_...`) get 403 `test_key_referrals_unavailable` until the referral sandbox opens; from then on a test key reaches sandbox referrals only, and a live key never sees them. Staged: it opens with review records, once their sweep is on and has finished its first pass; until then every call answers 404 `operation_not_open`.","parameters":[{"name":"id","in":"path","description":"The referral's id.","required":true,"schema":{"x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":128}},{"name":"version","in":"query","description":"The version to read, 1 for the record's first, written in digits. Leave it out, or blank, for the latest the record can show.","schema":{"x-data-class":"public","type":"integer","minimum":1,"maximum":2147483647}}],"responses":{"200":{"description":"The version.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewRecord"}}}},"400":{"description":"Refused. `code` is one of: `validation_failed`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["validation_failed"]},"401":{"description":"Refused. `code` is one of: `missing_api_key`, `invalid_api_key`, `api_key_expired`, `signature_profile_invalid`, `signature_expired`, `target_uri_not_allowed`, `signature_invalid`, `environment_mismatch`, `credential_not_activated`, `signed_requests_required`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["missing_api_key","invalid_api_key","api_key_expired","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signed_requests_required"]},"403":{"description":"Refused. `code` is one of: `key_not_scoped`, `provider_terminated`, `permission_denied`, `edge_auth_required`, `ip_not_allowed`, `test_key_referrals_unavailable`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["key_not_scoped","provider_terminated","permission_denied","edge_auth_required","ip_not_allowed","test_key_referrals_unavailable"]},"404":{"description":"Refused. `code` is one of: `operation_not_open`, `not_found`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["operation_not_open","not_found"]},"409":{"description":"Refused. `code` is one of: `signature_replay`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["signature_replay"]},"429":{"description":"Refused. `code` is one of: `rate_limited`, `auth_failures_throttled`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["rate_limited","auth_failures_throttled"]},"500":{"description":"Refused. `code` is one of: `internal_error`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["internal_error"]},"503":{"description":"Refused. `code` is one of: `credentials_unavailable`, `platform_standby`. Each code is described in `x-error-catalog`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Cache-Control":{"$ref":"#/components/headers/Cache-Control"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"x-error-codes":["credentials_unavailable","platform_standby"]}},"x-required-permission":"referrals:read","x-rate-class":"read","x-idempotency":{"kind":"none"},"x-availability":{"status":"staged","modes":["live","test"],"opens_with":"review_record"},"x-data-class":"borrower_personal"}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your API key from the portal's API page: `Authorization: Bearer <key>`. `GET /api/gap/v1/me` answers any key, live or test, with what it is, what it may do and when it expires. Keys come in two formats. A legacy key starts `sa_gap_` (`sa_gap_test_` for a test key). A v2 key, which the portal issues once v2 keys open, is `sa_live_` or `sa_test_`, then 30 letters and digits and a 6-character checksum; a v2 key whose checksum doesn't match gets 401 `missing_api_key` before any lookup. A v2 key holds only the permissions chosen when it was created, from a preset (Intake only, Intake + status, Reporting, Membership roster) or one by one: each operation names the permission it needs in `x-required-permission`, and a v2 key without it gets 403 `permission_denied`. If v2 keys can't be checked just now, the call gets 503 `credentials_unavailable`; retry after the seconds in `Retry-After`. A legacy key's scopes decide what it reaches: no scopes reaches everything; `referrals` the referral and analytics operations; `plans` the membership plan operations; `read` the plan list operations and analytics. Outside them it gets 403 `key_not_scoped`. Every key reaches the events feed, which lists each event type only to a key holding that type's permission, and an event's referral, beyond its id, only to a key holding `referrals:read`. Test keys (`sa_gap_test_...`) work only with the membership plan operations, `GET /api/gap/v1/me` and the events feed's plan events until the referral sandbox opens: until then a referral or analytics call with one gets 403 `test_key_referrals_unavailable`. Once it opens, a test key reaches sandbox referrals only, and a live key never sees them. A v2 test key (`sa_test_...`) is a test key in the same way. A test key expires at most 90 days after it is created, a v2 live key at most 365 days after, and a legacy live key only when we schedule its retirement, with notice; an expired key gets 401 `api_key_expired`. Each key may make 300 reads, 120 writes and 12 bulk calls a minute (`x-rate-class`); over that is 429 with `Retry-After`. A signed credential sends no bearer key: it signs each request instead (`signedRequest`). An institution can ask us to accept signed requests only: from then a call with an API key gets 401 `signed_requests_required`. A key, bearer or signed, can hold an IP allowlist: a call from an address outside it, as our edge saw it, gets 403 `ip_not_allowed`. A request that carries a `Signature-Input` header is read as a signed one, whatever else it carries, so a bearer call must carry none: with one, even a valid key's call gets 401 `signature_profile_invalid`. Such a call counts as failed authentication from your address, so past the limit it can be 429 `auth_failures_throttled`. Don't let a proxy, gateway or agent add an HTTP message signature of its own to your bearer calls."},"signedRequest":{"type":"apiKey","in":"header","name":"Signature","description":"A signed credential: the institution's own Ed25519 or ECDSA P-256 public key, registered in the portal as a JWK or a `BEGIN PUBLIC KEY` PEM (for P-256, a named-curve (prime256v1) SubjectPublicKeyInfo with an uncompressed point), signing every request with RFC 9421 HTTP Message Signatures and RFC 9530 Content-Digest (the gap-v2 profile). It holds the permissions chosen when it was registered, as a v2 key does, and expires as one does. A new credential is activated by one signed `POST /api/gap/v1/credentials/{id}/verify`; until then any other call gets 401 `credential_not_activated`. Each request carries `Signature-Input` and `Signature` (one signature, labelled `sa`), `X-SA-Environment` (`TEST` for a test credential, `PRODUCTION` for a live one) and no `Authorization` header: a request carrying both credentials is ambiguous, and is 401 `signature_profile_invalid` however valid either is. The signature covers `@method` and `@target-uri`, then `content-digest` and `idempotency-key` on POST and PATCH, `idempotency-key` on GET and HEAD whenever it is sent, then `x-sa-environment`. Its parameters are `created` (unix seconds), `nonce` (16 to 128 of `A-Z a-z 0-9 _ -`, new for every request), `keyid` (the credential's id), `alg` (`ed25519` or `ecdsa-p256-sha256`, the credential's own) and `tag=\"gap-v2\"`, in that order. `@target-uri` is `https://`, the host you called in lower case (`secondappraisal.com`, `gap.secondappraisal.com` or `lenders.secondappraisal.com`), then the path and query of the request URL as a WHATWG URL parser serializes it (`new URL()` in JavaScript), which is how we read every request: never the request line as your client wrote it. Percent-encode each query value with RFC 3986's unreserved characters only (a `'` is `%27`), percent-encode non-ASCII as its UTF-8 bytes, and send no empty `?` and no `.` or `..` path segment; a percent-escape you send is kept as sent, its hex digits' case included. On a signed request `Idempotency-Key` is 1 to 255 visible ASCII characters (`!` to `~`), no spaces. `Content-Digest` is `sha-256=:<base64>:` over the exact body bytes, a string's as UTF-8. An Ed25519 signature is its 64 bytes; a P-256 signature is the 64-byte r||s, never DER. Both are sent as `sa=:<base64>:`. `created` must be within 300 seconds before our clock and 30 after it. A nonce is used once per credential: it is spent when the signature has verified, the body has matched `Content-Digest`, the request has come from an address the credential's IP allowlist holds (when it holds one) and the credential was within its rate limit, whatever the request is then answered. A 403 `ip_not_allowed`, a 503 `credentials_unavailable` because the allowlist couldn't be decided, a 429 `rate_limited`, or a 503 `platform_standby` because the nonce itself couldn't be written, leaves it unspent. A retry is signed again, with a new nonce and the same `Idempotency-Key`. Refused before any credential is looked up: 401 `signature_profile_invalid`, `signature_expired` or `target_uri_not_allowed`. Refused the same way whatever failed: 401 `signature_invalid`, before any byte of the body is read, or once the signature verifies, for a body that doesn't match `Content-Digest`. Either counts as failed authentication from your address, a revoked credential's included, so past the limit it can be 429 `auth_failures_throttled`, and neither spends the nonce. Then, in order: 413 `payload_too_large` or 400 `invalid_json` for a body too large or too slow to arrive, which spends nothing either; 403 `ip_not_allowed` for an address outside the credential's IP allowlist, or 503 `credentials_unavailable` when the allowlist can't be decided, which spend nothing either; 429 `rate_limited`; 409 `signature_replay` for a nonce already used; 401 `api_key_expired`, `credential_not_activated` or `environment_mismatch`."}},"headers":{"X-Request-Id":{"description":"This request's id (`req_` and 32 hex characters). Every response the API itself produces carries it; quote it when you contact us. The platform's 503 during a failover has none.","schema":{"type":"string","pattern":"^req_[0-9a-f]{32}$"}},"Cache-Control":{"description":"`private, no-store` on every response the API itself produces, except this document's, which is `public, max-age=3600`.","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying: until the rate-limit window lets this key through again (429 `rate_limited`), until failed authentications from your address are back under their limit (429 `auth_failures_throttled`), until v2 keys can be checked again (503 `credentials_unavailable`), or until a failover has moved on (503 `platform_standby`).","schema":{"type":"integer","minimum":0}},"RateLimit":{"description":"Sent from a later release; until then no response carries it, and a client must work without it. What is left of each limit `RateLimit-Policy` names, in the form of the IETF RateLimit header fields draft (draft-ietf-httpapi-ratelimit-headers): the requests still available (`r`), and the seconds within which no more than those may be made (`t`). After a 429, wait as `Retry-After` says.","required":false,"schema":{"type":"string"}},"RateLimit-Policy":{"description":"Sent from a later release; until then no response carries it, and a client must work without it. The limits this call's key is held to (`x-rate-class`), in the form of the IETF RateLimit header fields draft (draft-ietf-httpapi-ratelimit-headers): each limit by name, with its quota of requests (`q`) and its window in seconds (`w`).","required":false,"schema":{"type":"string"}},"Deprecation":{"description":"RFC 9745: when the operation was deprecated, as `@<unix seconds>`.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594: when the operation stops answering (an HTTP date).","schema":{"type":"string"}},"Link":{"description":"`rel=\"deprecation\"`: where the migration is described.","schema":{"type":"string"}}},"schemas":{"AnalyticsReport":{"description":"Your referral funnel, settled outcomes, spend and breakdowns by market and carrier: the numbers the portal's Analytics page shows.","type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["gap.analytics"]},"filters":{"type":"object","properties":{"from":{"type":"string","format":"date-time","description":"The `from` you sent, as an instant.","x-data-class":"public","nullable":true},"to":{"type":"string","format":"date-time","description":"The `to` you sent, as an instant.","x-data-class":"public","nullable":true}},"required":["from","to"]},"funnel":{"type":"object","properties":{"submitted":{"x-data-class":"institution_confidential","type":"integer"},"awaiting_activation":{"x-data-class":"institution_confidential","type":"integer"},"active":{"x-data-class":"institution_confidential","type":"integer"},"settled":{"x-data-class":"institution_confidential","type":"integer"},"declined":{"x-data-class":"institution_confidential","type":"integer"},"unreachable":{"x-data-class":"institution_confidential","type":"integer"},"expired":{"x-data-class":"institution_confidential","type":"integer"},"cancelled":{"x-data-class":"institution_confidential","type":"integer"},"reporting_withdrawn":{"description":"Referrals whose borrower withdrew permission to report their progress to you: counted here and in `submitted`, never under a later status, in `activation_rate` or among the outcomes.","x-data-class":"institution_confidential","type":"integer"},"activation_rate":{"type":"number","description":"Activated ÷ (activated + closed without activation), 0 to 1; null before any referral closes.","x-data-class":"institution_confidential","nullable":true}},"required":["submitted","awaiting_activation","active","settled","declined","unreachable","expired","cancelled","reporting_withdrawn","activation_rate"]},"outcomes":{"type":"object","properties":{"settled_cases":{"x-data-class":"institution_confidential","type":"integer"},"total_uplift_cents":{"x-data-class":"institution_confidential","type":"integer"},"avg_uplift_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"total_exposure_before_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"total_exposure_after_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"total_exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"avg_exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["settled_cases","total_uplift_cents","avg_uplift_cents","total_exposure_before_cents","total_exposure_after_cents","total_exposure_reduction_cents","avg_exposure_reduction_cents"]},"spend":{"type":"object","properties":{"fees_paid_cents":{"x-data-class":"institution_confidential","type":"integer"},"fees_pending_cents":{"x-data-class":"institution_confidential","type":"integer"},"roi_multiple":{"type":"number","description":"Exposure reduction ÷ fees paid; null without both.","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"net_savings_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["fees_paid_cents","fees_pending_cents","roi_multiple","net_savings_cents"]},"by_market":{"type":"array","items":{"type":"object","properties":{"key":{"description":"The state code or carrier name; `unknown` when the referral has none.","x-data-class":"institution_confidential","type":"string"},"referrals":{"x-data-class":"institution_confidential","type":"integer"},"settled":{"x-data-class":"institution_confidential","type":"integer"},"uplift_cents":{"x-data-class":"institution_confidential","type":"integer"},"exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["key","referrals","settled","uplift_cents","exposure_reduction_cents"]}},"by_carrier":{"type":"array","items":{"type":"object","properties":{"key":{"description":"The state code or carrier name; `unknown` when the referral has none.","x-data-class":"institution_confidential","type":"string"},"referrals":{"x-data-class":"institution_confidential","type":"integer"},"settled":{"x-data-class":"institution_confidential","type":"integer"},"uplift_cents":{"x-data-class":"institution_confidential","type":"integer"},"exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["key","referrals","settled","uplift_cents","exposure_reduction_cents"]}},"time_series":{"type":"array","items":{"type":"object","properties":{"month":{"description":"The UTC month, YYYY-MM.","x-data-class":"public","type":"string"},"submitted":{"x-data-class":"institution_confidential","type":"integer"},"activated":{"x-data-class":"institution_confidential","type":"integer"},"settled":{"x-data-class":"institution_confidential","type":"integer"},"uplift_cents":{"x-data-class":"institution_confidential","type":"integer"},"exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["month","submitted","activated","settled","uplift_cents","exposure_reduction_cents"]}}},"required":["object","filters","funnel","outcomes","spend","by_market","by_carrier","time_series"]},"ApiKeyIntrospection":{"description":"The API key that made the call: what it is, what it may do, and when it expires. Never the key itself.","type":"object","properties":{"id":{"description":"The key's id, the one the portal's API page lists it under.","x-data-class":"institution_confidential","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["gap.api_key"]},"prefix":{"description":"The key's first characters, as the portal shows them: enough to tell your keys apart, never enough to use one. A signed credential's starts `pk:`, then the start of its public key's thumbprint.","x-data-class":"institution_confidential","type":"string"},"mode":{"description":"`live`, or `test` for a sandbox key. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["live","test"]},"format":{"description":"`legacy` for an `sa_gap_` key, `v2` for an `sa_live_` or `sa_test_` key or a signed credential. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["legacy","v2"]},"auth_method":{"description":"`secret`: the key is sent as a bearer token. `public_key`: a signed credential, whose requests are signed with its registered public key's private half. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["secret","public_key"]},"permissions":{"description":"What the key may do. A v2 key holds the permissions chosen when it was created. A legacy key's scopes are given as the permissions they reach: no scopes reach every one. `referrals.contact:read` and `members:activation-link` are field permissions: they open no operation, and a key without one reads null in each field whose `x-requires-permission` names it, in whatever its operations answer. A legacy `read` key lists `referrals.contact:read` for the analytics' figures built on the loan payoff and the roster's member emails, and a legacy `plans` key for the roster's member emails. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"institution_confidential","type":"array","items":{"type":"string","enum":["referrals:write","referrals:read","referrals.contact:read","reporting:read","plans:read","plans:write","members:activation-link"]}},"expires_at":{"type":"string","format":"date-time","description":"When the key stops working; null for a legacy live key with no retirement scheduled.","x-data-class":"institution_confidential","nullable":true},"institution":{"type":"object","properties":{"id":{"description":"Your institution's id.","x-data-class":"institution_confidential","type":"string"},"status":{"description":"`active`; `requested` before approval; `suspended`, which can read but not write; `terminated`. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["requested","active","suspended","terminated"]}},"required":["id","status"]},"allowed_cidr_count":{"description":"How many address ranges the key's IP allowlist holds; 0 when it has none.","x-data-class":"institution_confidential","type":"integer","minimum":0}},"required":["id","object","prefix","mode","format","auth_method","permissions","expires_at","institution","allowed_cidr_count"]},"BulkRowAcknowledgementError":{"type":"object","properties":{"field":{"description":"The row's field, `program`, `row` for the row as a whole, or `body` for a row that is not an object.","x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"},"code":{"description":"A field-error code (`unknown_field`, `invalid_type`, `required`, `prohibited_field`, …), or a refusal code such as `email_required_sms_disabled`, `script_version_stale`, `billing_required`, `idempotency_key_mode_conflict`, `idempotency_key_reused`, `state_not_served` or `duplicate_referral`.","x-data-class":"public","type":"string"},"state":{"description":"On `state_not_served`: the state judged.","x-data-class":"borrower_personal","type":"string"},"basis":{"description":"On `state_not_served`: `garaged` when the state judged is `garaged_state`, `loss` when it is `loss_state`. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["garaged","loss"]}},"required":["field","message"]},"BulkRowError":{"type":"object","properties":{"field":{"description":"The row's field, `program`, `row` for the row as a whole, or `body` for a row that is not an object.","x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"},"code":{"description":"A field-error code (`unknown_field`, `invalid_type`, `required`, `prohibited_field`, …), or a refusal code such as `email_required_sms_disabled`, `script_version_stale`, `billing_required`, `idempotency_key_mode_conflict`, `idempotency_key_reused`, `state_not_served` or `duplicate_referral`.","x-data-class":"public","type":"string"},"existing_referral_id":{"description":"On `duplicate_referral`: your referral for the same loss. Never sent to a key that doesn't hold `referrals:read`.","x-data-class":"institution_confidential","type":"string"},"state":{"description":"On `state_not_served`: the state judged.","x-data-class":"borrower_personal","type":"string"},"basis":{"description":"On `state_not_served`: `garaged` when the state judged is `garaged_state`, `loss` when it is `loss_state`. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["garaged","loss"]}},"required":["field","message"]},"CredentialActivation":{"description":"A signed credential, activated: it can sign any call its permissions reach.","type":"object","properties":{"id":{"description":"The signed credential's id.","x-data-class":"institution_confidential","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["gap.signed_credential"]},"mode":{"description":"`live`, or `test` for a sandbox credential. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["live","test"]},"alg":{"description":"The algorithm its key was registered with, which every signature it makes must name: `ed25519` or `ecdsa-p256-sha256`. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["ed25519","ecdsa-p256-sha256"]},"activated_at":{"description":"When the credential's first verification call succeeded. A later call answers the same time.","x-data-class":"institution_confidential","type":"string","format":"date-time"}},"required":["id","object","mode","alg","activated_at"]},"DisclosureInput":{"description":"Warm-handoff attestation. Accepted only with `consent_mode: \"warm_handoff\"`.","type":"object","properties":{"confirmed":{"description":"Must be `true`.","x-data-class":"institution_confidential","type":"boolean","enum":[true]},"channel":{"description":"How the script was delivered.","x-data-class":"institution_confidential","type":"string","enum":["phone","in_person","email","video","other"]},"attestor_name":{"description":"The name of the person on your staff who delivered the script.","x-data-class":"institution_confidential","type":"string","minLength":1,"maxLength":120},"delivered_on":{"type":"string","format":"date","description":"The date the script was delivered; omit for today. At most a day ahead (a timezone ahead of UTC) and no more than 90 days old. Spaces around it are ignored.","x-data-class":"institution_confidential","nullable":true},"script_version":{"type":"string","description":"The disclosure script version your staff delivered, as the script names it. The referral is stamped with it; omit it to attest the current script. A version that is not the current script is refused with 409 `script_version_stale` (a row-level error on bulk).","x-data-class":"institution_confidential","nullable":true,"example":"1.3"}},"required":["confirmed","channel","attestor_name"],"additionalProperties":false},"EventFeedPage":{"type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ThinEvent"}},"has_more":{"x-data-class":"public","type":"boolean"},"next_cursor":{"description":"Every page has one, the last and an empty one too: where your next poll resumes. Keep the latest and send it as `starting_after`; `has_more` says whether to fetch the next page now.","x-data-class":"institution_confidential","type":"string"}},"required":["object","data","has_more","next_cursor"]},"FieldError":{"description":"One field that failed validation.","type":"object","properties":{"field":{"description":"The field, in the API's own names: a dotted path inside the body (`program.subsidy_value`, `member.email`), or the query or path parameter's name. A rule across fields names the rule (`borrower_contact`). An error about the body as a whole is `body`: a body that is not an object, or an edit refused without naming one field.","x-data-class":"public","type":"string"},"code":{"description":"What is wrong with the field. Branch on this, not on `message`. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["unknown_field","unknown_parameter","duplicate_parameter","invalid_type","required","too_long","too_large","too_small","invalid_format","invalid_value","prohibited_field"]},"message":{"description":"A sentence a person can act on.","x-data-class":"public","type":"string"},"pointer":{"description":"RFC 6901 JSON Pointer to the value in the request body (`/program/mode`); `\"\"`, the whole body, when `field` is `body`. Absent for query and path parameters, and for a rule across fields such as `borrower_contact`, which names no member of the body.","x-data-class":"public","type":"string"}},"required":["field","code","message"]},"OpenApiDocument":{"description":"This document.","type":"object","properties":{"openapi":{"x-data-class":"public","type":"string"},"info":{"type":"object","properties":{"title":{"x-data-class":"public","type":"string"},"version":{"x-data-class":"public","type":"string"}},"required":["title","version"],"additionalProperties":{}}},"required":["openapi","info"],"additionalProperties":{}},"PlanEnrollment":{"description":"A membership enrollment (snake_case). Ignore fields you don't know.","type":"object","properties":{"id":{"x-data-class":"institution_confidential","type":"string"},"vin":{"x-data-class":"borrower_personal","type":"string"},"status":{"description":"PENDING (direct-collect, awaiting the member's activation), ACTIVE, PAST_DUE, LAPSED or CANCELLED. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","ACTIVE","PAST_DUE","LAPSED","CANCELLED"]},"billing_mode":{"description":"PARTNER_BILLED: you pay the wholesale price per vehicle-month and bill the member yourself. DIRECT_COLLECT: we charge the member's card your price and settle the difference with you monthly. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"institution_confidential","type":"string","enum":["PARTNER_BILLED","DIRECT_COLLECT"]},"external_ref":{"type":"string","x-data-class":"institution_confidential","nullable":true},"vehicle":{"type":"object","properties":{"year":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"make":{"type":"string","x-data-class":"borrower_personal","nullable":true},"model":{"type":"string","x-data-class":"borrower_personal","nullable":true},"garaged_state":{"type":"string","x-data-class":"borrower_personal","nullable":true}},"required":["year","make","model","garaged_state"]},"member":{"type":"object","properties":{"first_name":{"type":"string","x-data-class":"borrower_personal","nullable":true},"last_name":{"type":"string","x-data-class":"borrower_personal","nullable":true},"email":{"type":"string","x-data-class":"borrower_contact","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["first_name","last_name","email"]},"member_price_cents":{"description":"0 for partner-billed rows.","x-data-class":"institution_confidential","type":"integer"},"wholesale_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"started_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"eligible_from":{"type":"string","format":"date-time","description":"The instant from which a total loss on this vehicle qualifies for the member consultation (thirty days after the membership starts).","x-data-class":"institution_confidential","nullable":true},"current_period_end":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"cancel_at_period_end":{"x-data-class":"institution_confidential","type":"boolean"},"cancelled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"activation_url":{"type":"string","description":"Direct-collect only, while PENDING: the co-branded page where the member activates. Anyone holding it can activate, so send it only to the member.","x-data-class":"credential","x-requires-permission":"members:activation-link","nullable":true},"activation_expires_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"test":{"description":"Created through a test key.","x-data-class":"public","type":"boolean"},"created_at":{"x-data-class":"institution_confidential","type":"string","format":"date-time"}},"required":["id","vin","status","billing_mode","external_ref","vehicle","member","member_price_cents","wholesale_cents","started_at","eligible_from","current_period_end","cancel_at_period_end","cancelled_at","activation_url","activation_expires_at","test","created_at"]},"PlanEnrollmentBulkInput":{"type":"object","properties":{"enrollments":{"description":"1 to 500 vehicles. Each row is validated and enrolled on its own.","x-data-class":"borrower_contact","minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/PlanEnrollmentBulkRow"}}},"required":["enrollments"],"additionalProperties":false},"PlanEnrollmentBulkResult":{"type":"object","properties":{"received":{"x-data-class":"public","type":"integer"},"created":{"description":"Rows enrolled now (replays are not counted).","x-data-class":"public","type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"description":"0-based index into `enrollments`.","x-data-class":"public","type":"integer"},"vin":{"description":"The row's VIN as sent (normalized when valid).","x-data-class":"borrower_personal","type":"string"},"ok":{"x-data-class":"public","type":"boolean"},"id":{"x-data-class":"institution_confidential","type":"string"},"status":{"x-data-class":"public","type":"string","enum":["PENDING","ACTIVE","PAST_DUE","LAPSED","CANCELLED"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"idempotent_replay":{"x-data-class":"public","type":"boolean"},"code":{"description":"On a failed row: `validation_failed`, or the refusal code the single endpoint would answer.","x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"},"field_errors":{"type":"array","items":{"type":"object","properties":{"field":{"x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"},"code":{"x-data-class":"public","type":"string"},"pointer":{"x-data-class":"public","type":"string"}},"required":["field","message"]}}},"required":["index","vin","ok"]}}},"required":["received","created","results"]},"PlanEnrollmentBulkRow":{"description":"One vehicle in a bulk enrollment.","type":"object","properties":{"vin":{"description":"The 17-character VIN, case-insensitive. Checked before anything is enrolled: 17 characters, no I, O or Q, and a valid check digit (422 `vin_invalid`, or a field error).","x-data-class":"borrower_personal","type":"string","minLength":1,"example":"1FTFW1ET5DFC10312"},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true},"garaged_state":{"type":"string","description":"The two-letter state where the vehicle is garaged, case-insensitive. Required to enroll: a row without it is refused with 422 `state_required`, and a state where SecondAppraisal can't act as the appraiser with 422 `state_blocked`.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"member":{"x-data-class":"borrower_contact","anyOf":[{"$ref":"#/components/schemas/PlanMemberInput"},{"type":"object","nullable":true,"enum":[null]}]},"vehicle":{"x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/PlanVehicleInput"},{"type":"object","nullable":true,"enum":[null]}]},"consent_mode":{"type":"string","enum":["invitation","warm_handoff",null],"description":"Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"idempotency_key":{"type":"string","maxLength":255,"description":"Replays this row like the single endpoint's Idempotency-Key header. A row without one is keyed `<header>:<index>` when the call sends an Idempotency-Key header.","x-data-class":"institution_confidential","nullable":true}},"required":["vin"],"additionalProperties":false},"PlanEnrollmentCancelInput":{"description":"Cancel the enrollment. Nothing else may be sent with it.","type":"object","properties":{"cancel":{"description":"Cancels the enrollment: at once for a partner-billed row, at period end for a direct-collect row with a subscription.","x-data-class":"public","type":"boolean","enum":[true]}},"required":["cancel"],"additionalProperties":false},"PlanEnrollmentCreateResult":{"allOf":[{"$ref":"#/components/schemas/PlanEnrollment"},{"type":"object","properties":{"idempotent_replay":{"description":"True when this answers a replayed Idempotency-Key with the original enrollment.","x-data-class":"public","type":"boolean"}},"required":["idempotent_replay"]}]},"PlanEnrollmentEditInput":{"description":"Edit the reference or the member's contact details. A body with nothing to apply is a 400; `member: null`, `cancel: false` or null and `new_vin: null` apply nothing.","type":"object","properties":{"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true},"member":{"description":"Null is the same as leaving `member` out.","x-data-class":"borrower_contact","anyOf":[{"$ref":"#/components/schemas/PlanMemberInput"},{"type":"object","nullable":true,"enum":[null]}]},"cancel":{"type":"boolean","enum":[false,null],"description":"`false` or null is the same as leaving `cancel` out. To cancel, send `{ \"cancel\": true }` on its own.","x-data-class":"public","nullable":true},"new_vin":{"description":"Null is the same as leaving `new_vin` out. To swap the vehicle, send the new VIN.","x-data-class":"public","type":"string","nullable":true,"enum":[null]}},"additionalProperties":false},"PlanEnrollmentInput":{"description":"One vehicle to enroll. The VIN is the key, and the garaged state is required; everything else is optional. A vehicle that has been the subject of a total-loss consultation with SecondAppraisal cannot be enrolled (422 `vin_already_consulted`); a VIN with a live membership anywhere cannot be enrolled twice (409 `vin_already_live`).","type":"object","properties":{"vin":{"description":"The 17-character VIN, case-insensitive. Checked before anything is enrolled: 17 characters, no I, O or Q, and a valid check digit (422 `vin_invalid`, or a field error).","x-data-class":"borrower_personal","type":"string","minLength":1,"example":"1FTFW1ET5DFC10312"},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true},"garaged_state":{"type":"string","description":"The two-letter state where the vehicle is garaged, case-insensitive. Required to enroll: a row without it is refused with 422 `state_required`, and a state where SecondAppraisal can't act as the appraiser with 422 `state_blocked`.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"member":{"x-data-class":"borrower_contact","anyOf":[{"$ref":"#/components/schemas/PlanMemberInput"},{"type":"object","nullable":true,"enum":[null]}]},"vehicle":{"x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/PlanVehicleInput"},{"type":"object","nullable":true,"enum":[null]}]},"consent_mode":{"type":"string","enum":["invitation","warm_handoff",null],"description":"Case-insensitive.","x-data-class":"institution_confidential","nullable":true}},"required":["vin"],"additionalProperties":false},"PlanEnrollmentList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlanEnrollment"}},"next_cursor":{"type":"string","description":"Pass as `cursor` for the next page; null on the last page.","x-data-class":"institution_confidential","nullable":true}},"required":["data","next_cursor"]},"PlanEnrollmentPatchInput":{"description":"One of: `{ \"cancel\": true }` alone; a VIN swap (`new_vin`, with optional carry-over fields); or edits to `external_ref` and `member`.","anyOf":[{"$ref":"#/components/schemas/PlanEnrollmentCancelInput"},{"$ref":"#/components/schemas/PlanEnrollmentSwapInput"},{"$ref":"#/components/schemas/PlanEnrollmentEditInput"}]},"PlanEnrollmentSwapInput":{"description":"Move the member to another vehicle: the new VIN is enrolled and this enrollment closes. `garaged_state`, `external_ref` and each member field carry over unless sent; a paying direct-collect member's subscription moves to the new row.","type":"object","properties":{"new_vin":{"description":"The 17-character VIN, case-insensitive. Checked before anything is enrolled: 17 characters, no I, O or Q, and a valid check digit (422 `vin_invalid`, or a field error). The member moves to this vehicle, whose eligibility window starts again.","x-data-class":"borrower_personal","type":"string","minLength":1},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true},"garaged_state":{"type":"string","description":"The two-letter state where the vehicle is garaged, case-insensitive. Required to enroll: a row without it is refused with 422 `state_required`, and a state where SecondAppraisal can't act as the appraiser with 422 `state_blocked`.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"member":{"x-data-class":"borrower_contact","anyOf":[{"$ref":"#/components/schemas/PlanMemberInput"},{"type":"object","nullable":true,"enum":[null]}]},"vehicle":{"x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/PlanVehicleInput"},{"type":"object","nullable":true,"enum":[null]}]},"consent_mode":{"type":"string","enum":["invitation","warm_handoff",null],"description":"Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"cancel":{"type":"boolean","enum":[false,null],"description":"`false` or null is the same as leaving `cancel` out. To cancel, send `{ \"cancel\": true }` on its own.","x-data-class":"public","nullable":true}},"required":["new_vin"],"additionalProperties":false},"PlanEnrollmentSwapResult":{"allOf":[{"$ref":"#/components/schemas/PlanEnrollment"},{"type":"object","properties":{"swapped_from":{"description":"The enrollment this one replaced.","x-data-class":"institution_confidential","type":"string"}},"required":["swapped_from"]}]},"PlanLossNoticeInput":{"description":"A member vehicle was declared a total loss.","type":"object","properties":{"vin":{"description":"The member vehicle's VIN, case-insensitive.","x-data-class":"borrower_personal","type":"string","minLength":1},"date_of_loss":{"type":"string","format":"date","description":"YYYY-MM-DD; at most a day ahead (a timezone ahead of UTC). Null, or leaving it out, records no date. Spaces around it are ignored.","x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","maxLength":80,"x-data-class":"borrower_personal","nullable":true},"carrier":{"type":"string","maxLength":120,"description":"The member's insurer.","x-data-class":"borrower_personal","nullable":true}},"required":["vin"],"additionalProperties":false},"PlanLossNoticeResult":{"type":"object","properties":{"enrollment_id":{"x-data-class":"institution_confidential","type":"string"},"status":{"x-data-class":"public","type":"string","enum":["PENDING","ACTIVE","PAST_DUE","LAPSED","CANCELLED"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"eligible_from":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"recorded":{"x-data-class":"public","type":"boolean","enum":[true]}},"required":["enrollment_id","status","eligible_from","recorded"]},"PlanMemberInput":{"description":"The member's contact details. Every field is optional.","type":"object","properties":{"first_name":{"type":"string","maxLength":80,"x-data-class":"borrower_personal","nullable":true},"last_name":{"type":"string","maxLength":80,"x-data-class":"borrower_personal","nullable":true},"email":{"type":"string","maxLength":254,"description":"An email address of at most 254 characters, with no spaces, a single `@`, and a domain with a dot that has text on each side (`example.com`). Spaces around it are ignored. Stored in lowercase.","x-data-class":"borrower_contact","nullable":true},"phone":{"type":"string","maxLength":32,"x-data-class":"borrower_contact","nullable":true}},"additionalProperties":false},"PlanRedemption":{"description":"A member consultation drawn against your roster. Never the consultation's internals, and none of its progress once the borrower withdrew permission to report it.","type":"object","properties":{"id":{"x-data-class":"institution_confidential","type":"string"},"enrollment_id":{"x-data-class":"institution_confidential","type":"string"},"external_ref":{"type":"string","x-data-class":"institution_confidential","nullable":true},"referral_id":{"type":"string","x-data-class":"institution_confidential","nullable":true},"vin":{"x-data-class":"borrower_personal","type":"string"},"status":{"description":"Where the redemption stands: `PENDING`, `APPROVED`, `DENIED` or `COMPLETED`. `REPORTING_WITHDRAWN` once the borrower withdrew permission to report the progress of the referral it names (`referral_id`), whatever happens after: the referral itself then reads `status: reporting_withdrawn`. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","APPROVED","DENIED","COMPLETED","REPORTING_WITHDRAWN"]},"date_of_loss":{"type":"string","format":"date-time","x-data-class":"borrower_personal","nullable":true},"value_cents":{"x-data-class":"institution_confidential","type":"integer"},"initial_offer_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"final_settlement_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"uplift_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"settled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"created_at":{"x-data-class":"institution_confidential","type":"string","format":"date-time"},"reporting_withdrawn_at":{"type":"string","format":"date-time","description":"When the borrower withdrew permission to report the progress of the referral this redemption names; null while they haven't, or it names none. From then on the redemption shows `status: REPORTING_WITHDRAWN`, and its outcome (`initial_offer_cents`, `final_settlement_cents`, `uplift_cents`) and `settled_at` are null.","x-data-class":"institution_confidential","nullable":true}},"required":["id","enrollment_id","external_ref","referral_id","vin","status","date_of_loss","value_cents","initial_offer_cents","final_settlement_cents","uplift_cents","settled_at","created_at","reporting_withdrawn_at"]},"PlanRedemptionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlanRedemption"}},"next_cursor":{"type":"string","x-data-class":"institution_confidential","nullable":true}},"required":["data","next_cursor"]},"PlanRosterRejection":{"type":"object","properties":{"index":{"x-data-class":"public","type":"integer"},"vin":{"x-data-class":"borrower_personal","type":"string"},"field_errors":{"type":"array","items":{"type":"object","properties":{"field":{"x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"},"code":{"x-data-class":"public","type":"string"},"pointer":{"x-data-class":"public","type":"string"}},"required":["field","message"]}}},"required":["index","vin","field_errors"]},"PlanRosterSyncInput":{"type":"object","properties":{"enrollments":{"description":"Your full roster, at most 500 vehicles. An empty array cancels every live enrollment.","x-data-class":"borrower_contact","maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/PlanEnrollmentInput"}}},"required":["enrollments"],"additionalProperties":false},"PlanRosterSyncResult":{"type":"object","properties":{"received":{"x-data-class":"public","type":"integer"},"enrolled":{"description":"VINs enrolled now.","x-data-class":"borrower_personal","type":"array","items":{"type":"string"}},"already_live":{"description":"VINs that were already live.","x-data-class":"borrower_personal","type":"array","items":{"type":"string"}},"refused":{"type":"array","items":{"type":"object","properties":{"vin":{"x-data-class":"borrower_personal","type":"string"},"code":{"description":"The refusal code the single endpoint would answer.","x-data-class":"public","type":"string"},"message":{"x-data-class":"public","type":"string"}},"required":["vin","code","message"]}},"cancelled":{"description":"VINs cancelled because they were not in the roster.","x-data-class":"borrower_personal","type":"array","items":{"type":"string"}}},"required":["received","enrolled","already_live","refused","cancelled"]},"PlanStatement":{"description":"One calendar month's membership statement, in integer cents. `direction` says who owes whom: partner_owes (wholesale plus any member-fee subsidy, billed as one charge), we_owe_partner (the direct-collect spread above wholesale, remitted by transfer) or settled.","type":"object","properties":{"id":{"x-data-class":"institution_confidential","type":"string"},"period_start":{"x-data-class":"institution_confidential","type":"string","format":"date"},"period_end":{"x-data-class":"institution_confidential","type":"string","format":"date"},"status":{"x-data-class":"public","type":"string","enum":["ISSUED","SETTLED","VOID"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"vehicle_months":{"x-data-class":"institution_confidential","type":"number"},"active_vehicles_end":{"x-data-class":"institution_confidential","type":"integer"},"redemption_count":{"x-data-class":"institution_confidential","type":"integer"},"wholesale_cents":{"x-data-class":"institution_confidential","type":"integer"},"collected_cents":{"x-data-class":"institution_confidential","type":"integer"},"rev_share_cents":{"x-data-class":"institution_confidential","type":"integer"},"subsidy_cents":{"x-data-class":"institution_confidential","type":"integer"},"net_cents":{"description":"Positive: you owe us; negative: we owe you.","x-data-class":"institution_confidential","type":"integer"},"direction":{"x-data-class":"institution_confidential","type":"string","enum":["partner_owes","we_owe_partner","settled"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"charge_id":{"type":"string","x-data-class":"institution_confidential","nullable":true},"transfer_id":{"type":"string","x-data-class":"institution_confidential","nullable":true},"lines":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":{}},"description":"The statement's lines. Their fields are not frozen yet; ignore any you don't know.","x-data-class":"institution_confidential","nullable":true},"issued_at":{"x-data-class":"institution_confidential","type":"string","format":"date-time"},"settled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true}},"required":["id","period_start","period_end","status","vehicle_months","active_vehicles_end","redemption_count","wholesale_cents","collected_cents","rev_share_cents","subsidy_cents","net_cents","direction","charge_id","transfer_id","lines","issued_at","settled_at"]},"PlanStatementList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlanStatement"}}},"required":["data"]},"PlanVehicleInput":{"description":"The vehicle's description. Every field is optional.","type":"object","properties":{"year":{"type":"integer","minimum":1901,"maximum":2099,"x-data-class":"borrower_personal","nullable":true},"make":{"type":"string","maxLength":60,"x-data-class":"borrower_personal","nullable":true},"model":{"type":"string","maxLength":80,"x-data-class":"borrower_personal","nullable":true},"trim":{"type":"string","maxLength":80,"x-data-class":"borrower_personal","nullable":true}},"additionalProperties":false},"Problem":{"description":"RFC 9457 problem details, sent as `application/problem+json`, with the API's legacy `error` string and a stable `code` beside the standard members. Some operations add members of their own, named in their responses.","type":"object","properties":{"error":{"description":"The reason, in the wording the API has always used. Kept for existing clients; branch on `code`.","x-data-class":"public","type":"string"},"code":{"description":"Stable reason code. It never changes with the wording; `x-error-catalog` lists every code. New values may be added: treat a code you don't recognise by the response's HTTP status.","x-data-class":"public","type":"string","enum":["invalid_json","unsupported_media_type","payload_too_large","too_many_rows","prohibited_field","validation_failed","operation_not_open","idempotency_key_too_long","internal_error","missing_api_key","invalid_api_key","api_key_expired","key_not_scoped","test_key_referrals_unavailable","provider_terminated","provider_suspended","provider_not_active","rate_limited","auth_unavailable","permission_denied","credentials_unavailable","signed_requests_required","ip_not_allowed","auth_failures_throttled","signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch","credential_not_activated","signature_replay","public_key_in_use","edge_auth_required","platform_standby","membership_program_closed","membership_not_enabled","membership_rider_unsigned","membership_billing_mode_required","membership_billing_method_required","msa_required","billing_required","email_required_sms_disabled","script_version_stale","duplicate_reference","invalid_cursor","not_found","referral_locked","cancel_not_allowed","economics_locked","nothing_to_update","idempotency_key_mode_conflict","state_not_served","duplicate_referral","simulate_requires_test_key","invalid_transition","reporting_withdrawn","vin_already_live","vin_already_consulted","state_required","state_blocked","vin_invalid","invalid_customer_price","idempotency_key_reused","plan_not_enabled","not_live","not_a_member_vehicle","request_refused"]},"type":{"description":"RFC 9457 problem type: a URI naming the code. It identifies the problem; there is no need to fetch it.","x-data-class":"public","type":"string"},"title":{"description":"RFC 9457: a short summary of the code.","x-data-class":"public","type":"string"},"status":{"description":"RFC 9457: the HTTP status, repeated.","x-data-class":"public","type":"integer","minimum":400,"maximum":599},"detail":{"description":"RFC 9457: what went wrong with this request.","x-data-class":"public","type":"string"},"instance":{"description":"RFC 9457: the path that was called, without its query string.","x-data-class":"public","type":"string"},"request_id":{"description":"The request id, also sent as the `X-Request-Id` header. Quote it when you contact us.","x-data-class":"public","type":"string"},"field_errors":{"description":"On `validation_failed` and `prohibited_field`: each field that failed, and why.","x-data-class":"public","type":"array","items":{"$ref":"#/components/schemas/FieldError"}}},"required":["error","code","type","title","status","detail","instance","request_id"]},"Referral":{"description":"A referral as the API returns it: snake_case, grouped sub-objects. Ignore fields you don't know.","type":"object","properties":{"id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["gap.referral"]},"status":{"description":"Where the referral stands. `reporting_withdrawn` once the borrower withdrew permission to report its progress to you, whatever happens to it after: from then on it shows the fields you supplied and no progress. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["submitted","invited","handoff_pending","outreach_queued","contact_attempted","activated","in_progress","settled","closed","declined","unreachable","expired","cancelled","reporting_withdrawn"]},"status_label":{"description":"The status as the portal shows it.","x-data-class":"public","type":"string"},"status_tone":{"x-data-class":"public","type":"string","enum":["green","yellow","red","neutral"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"status_note":{"type":"string","description":"A third-person note on a terminal status, such as why it closed.","x-data-class":"institution_confidential","nullable":true},"external_ref":{"type":"string","description":"Your own reference, as you sent it.","x-data-class":"institution_confidential","nullable":true},"borrower":{"type":"object","properties":{"first_name":{"x-data-class":"borrower_personal","type":"string"},"last_name":{"x-data-class":"borrower_personal","type":"string"},"email":{"type":"string","x-data-class":"borrower_contact","x-requires-permission":"referrals.contact:read","nullable":true},"phone":{"type":"string","description":"10 digits.","x-data-class":"borrower_contact","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["first_name","last_name","email","phone"]},"vehicle":{"type":"object","properties":{"vin":{"type":"string","x-data-class":"borrower_personal","nullable":true},"year":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"make":{"type":"string","x-data-class":"borrower_personal","nullable":true},"model":{"type":"string","x-data-class":"borrower_personal","nullable":true}},"required":["vin","year","make","model"]},"claim":{"type":"object","properties":{"carrier":{"type":"string","x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","x-data-class":"borrower_personal","nullable":true},"loss_state":{"type":"string","x-data-class":"borrower_personal","nullable":true},"date_of_loss":{"type":"string","format":"date-time","description":"Midnight UTC on the date of loss.","x-data-class":"borrower_personal","nullable":true},"initial_offer_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true}},"required":["carrier","claim_number","loss_state","date_of_loss","initial_offer_cents"]},"liability":{"type":"object","properties":{"loan_payoff_cents":{"type":"integer","x-data-class":"borrower_personal","x-requires-permission":"referrals.contact:read","nullable":true},"deductible_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true}},"required":["loan_payoff_cents","deductible_cents"]},"program":{"type":"object","properties":{"mode":{"x-data-class":"institution_confidential","type":"string","enum":["provider_paid","split_pay","customer_paid","membership"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"subsidy_type":{"type":"string","enum":["percent","fixed_cents",null],"x-data-class":"institution_confidential","description":"New values may be added: treat one you don't recognise as unknown, and don't fail.","nullable":true},"subsidy_value":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"price_cents":{"description":"Your price for this referral's consultation, in cents, fixed when the referral was submitted. `mode` and the subsidy terms decide how much of it you pay.","x-data-class":"institution_confidential","type":"integer"},"locked":{"type":"boolean","description":"True once the borrower activated under these terms; they can no longer change. Null once reporting is withdrawn (`status: reporting_withdrawn`).","x-data-class":"institution_confidential","nullable":true}},"required":["mode","subsidy_type","subsidy_value","price_cents","locked"]},"consent":{"type":"object","properties":{"mode":{"x-data-class":"institution_confidential","type":"string","enum":["invitation","warm_handoff"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"disclosure_confirmed_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"disclosure_channel":{"type":"string","x-data-class":"institution_confidential","nullable":true},"disclosure_attestor_name":{"type":"string","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true}},"required":["mode","disclosure_confirmed_at","disclosure_channel","disclosure_attestor_name"]},"submitted_via":{"description":"Where the referral came from: `api`, `dashboard` or `csv`.","x-data-class":"public","type":"string"},"created_at":{"x-data-class":"institution_confidential","type":"string","format":"date-time"},"invited_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"invitation_expires_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"activated_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"settled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"closed_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"reporting_withdrawn_at":{"type":"string","format":"date-time","description":"When the borrower withdrew permission to report this referral's progress to you; null while they haven't. From then on the referral shows `status: reporting_withdrawn` and no progress: its invited, expiry, activated, settled and closed times and `program.locked` are null.","x-data-class":"institution_confidential","nullable":true}},"required":["id","object","status","status_label","status_tone","status_note","external_ref","borrower","vehicle","claim","liability","program","consent","submitted_via","created_at","invited_at","invitation_expires_at","activated_at","settled_at","closed_at","reporting_withdrawn_at"]},"ReferralAcknowledgement":{"description":"What a write answers a key that doesn't hold `referrals:read`: the referral's id and status, never its data. A key that holds `referrals:read` reads the referral itself.","type":"object","properties":{"id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["gap.referral"]},"status":{"x-data-class":"public","type":"string","enum":["submitted","invited","handoff_pending","outreach_queued","contact_attempted","activated","in_progress","settled","closed","declined","unreachable","expired","cancelled","reporting_withdrawn"],"description":"New values may be added: show one you don't recognise as not yet known, and don't fail."}},"required":["id","object","status"]},"ReferralBulkAcknowledgement":{"type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["bulk_result"]},"created":{"description":"Rows created now (replays are not counted).","x-data-class":"public","type":"integer"},"failed":{"x-data-class":"public","type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"description":"0-based index into `referrals`.","x-data-class":"public","type":"integer"},"ok":{"x-data-class":"public","type":"boolean"},"referral":{"$ref":"#/components/schemas/ReferralAcknowledgement"},"idempotent_replay":{"x-data-class":"public","type":"boolean"},"errors":{"type":"array","items":{"$ref":"#/components/schemas/BulkRowAcknowledgementError"}}},"required":["index","ok"]}}},"required":["object","created","failed","results"]},"ReferralBulkInput":{"type":"object","properties":{"referrals":{"description":"1 to 500 referrals. Each row is validated and created on its own.","x-data-class":"borrower_contact","minItems":1,"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/ReferralBulkRow"}}},"required":["referrals"],"additionalProperties":false},"ReferralBulkResult":{"type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["bulk_result"]},"created":{"description":"Rows created now (replays are not counted).","x-data-class":"public","type":"integer"},"failed":{"x-data-class":"public","type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"description":"0-based index into `referrals`.","x-data-class":"public","type":"integer"},"ok":{"x-data-class":"public","type":"boolean"},"referral":{"$ref":"#/components/schemas/Referral"},"idempotent_replay":{"x-data-class":"public","type":"boolean"},"errors":{"type":"array","items":{"$ref":"#/components/schemas/BulkRowError"}}},"required":["index","ok"]}}},"required":["object","created","failed","results"]},"ReferralBulkRow":{"description":"One referral in a bulk create.","type":"object","properties":{"borrower_first_name":{"x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80,"example":"Jordan"},"borrower_last_name":{"x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80,"example":"Reyes"},"borrower_email":{"type":"string","maxLength":254,"description":"An email address of at most 254 characters, with no spaces, a single `@`, and a domain with a dot that has text before it and at least two characters after it (`example.com`). Spaces around it are ignored. Stored in lowercase. At least one of `borrower_email` and `borrower_phone` is required. An invitation referral (the default `consent_mode`) needs an email while text invitations are off for its loss state: one without it is refused with 422 `email_required_sms_disabled`. A warm-handoff referral may be phone-only.","x-data-class":"borrower_contact","nullable":true,"example":"jordan.reyes@example.com"},"borrower_phone":{"type":"string","description":"A 10-digit US number; punctuation and a leading 1 are tolerated, and it is stored as 10 digits.","x-data-class":"borrower_contact","nullable":true,"example":"(512) 555-0142"},"consent_mode":{"type":"string","enum":["invitation","warm_handoff",null],"description":"`invitation` (the default): we send the borrower a co-branded activation link. `warm_handoff`: your staff delivered the disclosure script and the borrower agreed to be contacted; send the attestation as `disclosure`. Case-insensitive.","x-data-class":"institution_confidential","default":"invitation","nullable":true},"vin":{"type":"string","description":"11 to 17 letters and digits, never I, O or Q. Case-insensitive; stored in uppercase.","x-data-class":"borrower_personal","nullable":true,"example":"1FTFW1ET5DFC10312"},"vehicle_year":{"type":"integer","minimum":1950,"description":"A model year from 1950 to two years after the current year.","x-data-class":"borrower_personal","nullable":true,"example":2021},"vehicle_make":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"vehicle_model":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"primary_carrier":{"type":"string","maxLength":120,"description":"The borrower's insurer.","x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","maxLength":120,"description":"The borrower's claim number with that insurer.","x-data-class":"borrower_personal","nullable":true},"loss_state":{"type":"string","description":"The two-letter state of the loss, case-insensitive (`tx` is `TX`).","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"garaged_state":{"type":"string","description":"The two-letter USPS code of the state where the vehicle is garaged (a state or DC), case-insensitive. We judge this state when it is sent, else `loss_state`: a referral for a state we don't serve may be refused with 422 `state_not_served`.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"date_of_loss":{"type":"string","format":"date","description":"The date of the loss; at most a day ahead (a timezone ahead of UTC). Spaces around it are ignored.","x-data-class":"borrower_personal","nullable":true,"example":"2026-06-15"},"initial_offer_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The insurer's initial ACV offer, in cents.","x-data-class":"borrower_personal","nullable":true},"loan_payoff_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The outstanding loan payoff, in cents. It drives your exposure figures and stays editable after activation.","x-data-class":"borrower_personal","nullable":true},"deductible_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The deductible, in cents. Stays editable after activation.","x-data-class":"borrower_personal","nullable":true},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true,"example":"CASE-4471"},"program":{"description":"Per-referral program terms. Omit, or send null, to use your program defaults.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/ReferralProgramInput"},{"type":"object","nullable":true,"enum":[null]}]},"disclosure":{"description":"Warm-handoff attestation, only with `consent_mode: \"warm_handoff\"`. With it the referral is created ready for our outreach; without it the referral waits for your attestation.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/DisclosureInput"},{"type":"object","nullable":true,"enum":[null]}]},"claim_against":{"type":"string","enum":["own","other_driver","unknown",null],"description":"Whose insurer is handling the claim: `own` (the borrower's own insurer), `other_driver` (another driver's insurer), or `unknown`. Case-insensitive. Leave it out when you don't know.","x-data-class":"borrower_personal","nullable":true},"cause_of_loss":{"type":"string","enum":["collision","theft","fire","flood","weather","vandalism","animal","other",null],"description":"The cause of the loss, if you know it. `weather` is hail, wind or a storm; a flood is `flood`. Case-insensitive.","x-data-class":"borrower_personal","nullable":true},"trigger":{"type":"string","enum":["payoff_request","loss_notice","gap_claim","borrower_request","other",null],"description":"What led you to refer: `payoff_request` (an insurer asked you for the loan payoff), `loss_notice` (a notice of loss), `gap_claim` (the borrower filed a GAP claim), `borrower_request` (the borrower asked), or `other`. Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"payoff_requested_at":{"type":"string","description":"When the insurer asked you for the loan payoff: a date (`2026-06-16`, read as midnight UTC) or an ISO 8601 timestamp with its UTC offset (`2026-06-16T15:04:00-06:00`). At most a day ahead.","x-data-class":"institution_confidential","nullable":true,"example":"2026-06-16T15:04:00-06:00"},"payoff_request_channel":{"type":"string","enum":["phone","fax","email","mail","web_portal","electronic_service","other",null],"description":"How that payoff request reached you. `web_portal` is your own online portal; `electronic_service` is a third-party payoff or letter-of-guarantee service the insurer used. Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"client_name":{"type":"string","maxLength":120,"description":"For a GAP administrator: the lender or dealer whose borrower this is. At most 120 characters.","x-data-class":"institution_confidential","nullable":true},"requirement_basis":{"description":"Send it only when the borrower's contract lets you require the review. It is recorded and checked against your program's bases; messages to the borrower still ask rather than require.","x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/RequirementBasisInput"},{"type":"object","nullable":true,"enum":[null]}]},"idempotency_key":{"type":"string","maxLength":255,"description":"Makes this row safe to retry: a row with a key already used by your institution, in this key's mode, returns the original referral with `idempotent_replay: true`. A string of at most 255 characters; a longer key is refused, never cut. A live key can't send one that starts with `test:` (the row fails with `idempotency_key_mode_conflict`).","x-data-class":"institution_confidential","nullable":true}},"required":["borrower_first_name","borrower_last_name"],"additionalProperties":false},"ReferralCancelInput":{"description":"Cancel the referral. Nothing else may be sent with it.","type":"object","properties":{"action":{"description":"Cancels the referral. Allowed only before the borrower activates (409 after).","x-data-class":"public","type":"string","enum":["cancel"]}},"required":["action"],"additionalProperties":false},"ReferralCreateAcknowledgement":{"allOf":[{"$ref":"#/components/schemas/ReferralAcknowledgement"},{"type":"object","properties":{"idempotent_replay":{"description":"True when this answers a replay of this same request under its Idempotency-Key.","x-data-class":"public","type":"boolean"}},"required":["idempotent_replay"]}]},"ReferralCreateInput":{"description":"One borrower referral. Rules across fields: at least one of `borrower_email` and `borrower_phone`; a split-pay `program` needs both subsidy terms; `disclosure` only with a warm handoff.","type":"object","properties":{"borrower_first_name":{"x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80,"example":"Jordan"},"borrower_last_name":{"x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80,"example":"Reyes"},"borrower_email":{"type":"string","maxLength":254,"description":"An email address of at most 254 characters, with no spaces, a single `@`, and a domain with a dot that has text before it and at least two characters after it (`example.com`). Spaces around it are ignored. Stored in lowercase. At least one of `borrower_email` and `borrower_phone` is required. An invitation referral (the default `consent_mode`) needs an email while text invitations are off for its loss state: one without it is refused with 422 `email_required_sms_disabled`. A warm-handoff referral may be phone-only.","x-data-class":"borrower_contact","nullable":true,"example":"jordan.reyes@example.com"},"borrower_phone":{"type":"string","description":"A 10-digit US number; punctuation and a leading 1 are tolerated, and it is stored as 10 digits.","x-data-class":"borrower_contact","nullable":true,"example":"(512) 555-0142"},"consent_mode":{"type":"string","enum":["invitation","warm_handoff",null],"description":"`invitation` (the default): we send the borrower a co-branded activation link. `warm_handoff`: your staff delivered the disclosure script and the borrower agreed to be contacted; send the attestation as `disclosure`. Case-insensitive.","x-data-class":"institution_confidential","default":"invitation","nullable":true},"vin":{"type":"string","description":"11 to 17 letters and digits, never I, O or Q. Case-insensitive; stored in uppercase.","x-data-class":"borrower_personal","nullable":true,"example":"1FTFW1ET5DFC10312"},"vehicle_year":{"type":"integer","minimum":1950,"description":"A model year from 1950 to two years after the current year.","x-data-class":"borrower_personal","nullable":true,"example":2021},"vehicle_make":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"vehicle_model":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"primary_carrier":{"type":"string","maxLength":120,"description":"The borrower's insurer.","x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","maxLength":120,"description":"The borrower's claim number with that insurer.","x-data-class":"borrower_personal","nullable":true},"loss_state":{"type":"string","description":"The two-letter state of the loss, case-insensitive (`tx` is `TX`).","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"garaged_state":{"type":"string","description":"The two-letter USPS code of the state where the vehicle is garaged (a state or DC), case-insensitive. We judge this state when it is sent, else `loss_state`: a referral for a state we don't serve may be refused with 422 `state_not_served`.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"date_of_loss":{"type":"string","format":"date","description":"The date of the loss; at most a day ahead (a timezone ahead of UTC). Spaces around it are ignored.","x-data-class":"borrower_personal","nullable":true,"example":"2026-06-15"},"initial_offer_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The insurer's initial ACV offer, in cents.","x-data-class":"borrower_personal","nullable":true},"loan_payoff_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The outstanding loan payoff, in cents. It drives your exposure figures and stays editable after activation.","x-data-class":"borrower_personal","nullable":true},"deductible_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The deductible, in cents. Stays editable after activation.","x-data-class":"borrower_personal","nullable":true},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true,"example":"CASE-4471"},"program":{"description":"Per-referral program terms. Omit, or send null, to use your program defaults.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/ReferralProgramInput"},{"type":"object","nullable":true,"enum":[null]}]},"disclosure":{"description":"Warm-handoff attestation, only with `consent_mode: \"warm_handoff\"`. With it the referral is created ready for our outreach; without it the referral waits for your attestation.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/DisclosureInput"},{"type":"object","nullable":true,"enum":[null]}]},"claim_against":{"type":"string","enum":["own","other_driver","unknown",null],"description":"Whose insurer is handling the claim: `own` (the borrower's own insurer), `other_driver` (another driver's insurer), or `unknown`. Case-insensitive. Leave it out when you don't know.","x-data-class":"borrower_personal","nullable":true},"cause_of_loss":{"type":"string","enum":["collision","theft","fire","flood","weather","vandalism","animal","other",null],"description":"The cause of the loss, if you know it. `weather` is hail, wind or a storm; a flood is `flood`. Case-insensitive.","x-data-class":"borrower_personal","nullable":true},"trigger":{"type":"string","enum":["payoff_request","loss_notice","gap_claim","borrower_request","other",null],"description":"What led you to refer: `payoff_request` (an insurer asked you for the loan payoff), `loss_notice` (a notice of loss), `gap_claim` (the borrower filed a GAP claim), `borrower_request` (the borrower asked), or `other`. Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"payoff_requested_at":{"type":"string","description":"When the insurer asked you for the loan payoff: a date (`2026-06-16`, read as midnight UTC) or an ISO 8601 timestamp with its UTC offset (`2026-06-16T15:04:00-06:00`). At most a day ahead.","x-data-class":"institution_confidential","nullable":true,"example":"2026-06-16T15:04:00-06:00"},"payoff_request_channel":{"type":"string","enum":["phone","fax","email","mail","web_portal","electronic_service","other",null],"description":"How that payoff request reached you. `web_portal` is your own online portal; `electronic_service` is a third-party payoff or letter-of-guarantee service the insurer used. Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"client_name":{"type":"string","maxLength":120,"description":"For a GAP administrator: the lender or dealer whose borrower this is. At most 120 characters.","x-data-class":"institution_confidential","nullable":true},"requirement_basis":{"description":"Send it only when the borrower's contract lets you require the review. It is recorded and checked against your program's bases; messages to the borrower still ask rather than require.","x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/RequirementBasisInput"},{"type":"object","nullable":true,"enum":[null]}]}},"required":["borrower_first_name","borrower_last_name"],"additionalProperties":false},"ReferralCreateResult":{"allOf":[{"$ref":"#/components/schemas/Referral"},{"type":"object","properties":{"idempotent_replay":{"description":"True when this answers a replayed Idempotency-Key with the original referral.","x-data-class":"public","type":"boolean"}},"required":["idempotent_replay"]}]},"ReferralDetail":{"description":"A referral with the provider-facing milestone timeline and the financial panel (offers, settlement, exposure before and after, net savings, ROI).","allOf":[{"$ref":"#/components/schemas/Referral"},{"type":"object","properties":{"consultation_number":{"type":"string","description":"Our consultation number, once the borrower activates.","x-data-class":"institution_confidential","nullable":true},"days_in_negotiation":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"timeline":{"type":"object","properties":{"current_milestone":{"type":"string","enum":["referral_submitted","borrower_invited","disclosure_attested","outreach_in_progress","borrower_activated","valuation_report_received","insurance_details_received","photos_received","market_research","preliminary_estimate_ready","appraisal_report_complete","authorization_received","insurer_notified","insurer_acknowledged","insurer_appraiser_appointed","appraisals_exchanged","umpire_engaged","award_letter_issued","settlement_finalized","outcome_recorded",null],"x-data-class":"public","description":"New values may be added: show one you don't recognise as not yet known, and don't fail.","nullable":true},"terminal":{"type":"object","properties":{"status":{"x-data-class":"public","type":"string","enum":["submitted","invited","handoff_pending","outreach_queued","contact_attempted","activated","in_progress","settled","closed","declined","unreachable","expired","cancelled"],"description":"New values may be added: show one you don't recognise as not yet known, and don't fail."},"label":{"x-data-class":"public","type":"string"},"detail":{"x-data-class":"public","type":"string"}},"required":["status","label","detail"],"nullable":true},"phases":{"type":"array","items":{"type":"object","properties":{"key":{"x-data-class":"public","type":"string","enum":["referral","setup","research","negotiation","settlement"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"label":{"x-data-class":"public","type":"string"},"state":{"x-data-class":"public","type":"string","enum":["complete","current","upcoming","skipped"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"milestones":{"type":"array","items":{"$ref":"#/components/schemas/TimelineMilestone"}}},"required":["key","label","state","milestones"]}}},"required":["current_milestone","terminal","phases"],"description":"The provider-facing milestone timeline. Null once reporting is withdrawn (`status: reporting_withdrawn`).","nullable":true},"financial":{"type":"object","properties":{"initial_offer_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"current_best_offer_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"appraised_value_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"final_settlement_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"loan_payoff_cents":{"type":"integer","x-data-class":"borrower_personal","x-requires-permission":"referrals.contact:read","nullable":true},"deductible_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"fees_paid_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"gross_uplift_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"exposure_before_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"exposure_after_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"exposure_reduction_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"net_savings_cents":{"type":"integer","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"roi_multiple":{"type":"number","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"figures_are_final":{"x-data-class":"public","type":"boolean"},"savings_statement":{"type":"string","x-data-class":"institution_confidential","x-requires-permission":"referrals.contact:read","nullable":true},"payoff_missing":{"x-data-class":"public","type":"boolean"}},"required":["initial_offer_cents","current_best_offer_cents","appraised_value_cents","final_settlement_cents","loan_payoff_cents","deductible_cents","fees_paid_cents","gross_uplift_cents","exposure_before_cents","exposure_after_cents","exposure_reduction_cents","net_savings_cents","roi_multiple","figures_are_final","savings_statement","payoff_missing"],"description":"The financial panel. Null once reporting is withdrawn (`status: reporting_withdrawn`).","nullable":true}},"required":["consultation_number","days_in_negotiation","timeline","financial"]}]},"ReferralList":{"type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Referral"}},"has_more":{"x-data-class":"public","type":"boolean"},"next_cursor":{"type":"string","description":"Pass as `starting_after` for the next page; null on the last page. With `updated_after` it is never null: `has_more` says whether to fetch the next page now, and the last page's cursor is where your next poll resumes.","x-data-class":"institution_confidential","nullable":true}},"required":["object","data","has_more","next_cursor"]},"ReferralPatchInput":{"description":"Either `{ \"action\": \"cancel\" }` alone, or field edits.","anyOf":[{"$ref":"#/components/schemas/ReferralCancelInput"},{"$ref":"#/components/schemas/ReferralUpdateInput"}]},"ReferralProgramEditInput":{"description":"New program terms for a referral, as on create, except that `mode` and `subsidy_type` are matched as sent: a value with spaces around it is refused. A mode without subsidy terms inherits your default subsidy only when it is split-pay.","type":"object","properties":{"mode":{"description":"Who pays for the appraisal. Case-insensitive. Matched as sent: a value with spaces around it is refused.","x-data-class":"institution_confidential","type":"string","enum":["provider_paid","split_pay","customer_paid"]},"subsidy_type":{"type":"string","enum":["percent","fixed_cents",null],"description":"Split-pay only: how `subsidy_value` reads. Case-insensitive. Matched as sent: a value with spaces around it is refused.","x-data-class":"institution_confidential","nullable":true},"subsidy_value":{"type":"integer","description":"Split-pay only: the percent of the fee you cover (1 to 100), or the cents you cover, per `subsidy_type`. A split-pay referral needs both `subsidy_type` and a positive `subsidy_value`.","x-data-class":"institution_confidential","nullable":true}},"additionalProperties":false},"ReferralProgramInput":{"description":"Per-referral program terms, overriding your program defaults. A mode without subsidy terms inherits your default subsidy only when it is split-pay.","type":"object","properties":{"mode":{"description":"Who pays for the appraisal. Case-insensitive.","x-data-class":"institution_confidential","type":"string","enum":["provider_paid","split_pay","customer_paid"]},"subsidy_type":{"type":"string","enum":["percent","fixed_cents",null],"description":"Split-pay only: how `subsidy_value` reads. Case-insensitive.","x-data-class":"institution_confidential","nullable":true},"subsidy_value":{"type":"integer","description":"Split-pay only: the percent of the fee you cover (1 to 100), or the cents you cover, per `subsidy_type`. A split-pay referral needs both `subsidy_type` and a positive `subsidy_value`.","x-data-class":"institution_confidential","nullable":true}},"additionalProperties":false},"ReferralSimulateInput":{"description":"One simulated step for a sandbox referral (test keys only).","type":"object","properties":{"to":{"description":"The status to move the sandbox referral to; a step off its lifecycle is a 409 `invalid_transition`.","x-data-class":"public","type":"string","enum":["invited","activated","in_progress","settled","closed","declined","unreachable","expired"]},"outcome":{"description":"With `to: \"settled\"` only, and required there: the figures to settle with. The uplift and the exposure figures are computed from them as for a live settlement.","x-data-class":"borrower_personal","type":"object","properties":{"appraised_value_cents":{"x-data-class":"borrower_personal","type":"integer","minimum":0,"maximum":1000000000},"final_settlement_cents":{"x-data-class":"borrower_personal","type":"integer","minimum":0,"maximum":1000000000}},"required":["final_settlement_cents"],"additionalProperties":false}},"required":["to"],"additionalProperties":false},"ReferralUpdateInput":{"description":"Edit a referral. `loan_payoff_cents` and `deductible_cents` stay editable after activation; every other field locks when the borrower activates (409 `referral_locked`). Null or \"\" clears an optional field. Only spaces clears `vehicle_make`, `vehicle_model`, `primary_carrier`, `claim_number` and `external_ref` too, and is refused for the cents, the year, the email, the phone, the VIN and the loss and garaged states. The borrower's names can't be cleared, and `program: null` changes nothing.","type":"object","properties":{"loan_payoff_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"Editable any time. Null or \"\" clears it.","x-data-class":"borrower_personal","nullable":true},"deductible_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"Editable any time. Null or \"\" clears it.","x-data-class":"borrower_personal","nullable":true},"borrower_first_name":{"description":"At most 80 characters, as on create. It can't be cleared.","x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80},"borrower_last_name":{"description":"At most 80 characters, as on create. It can't be cleared.","x-data-class":"borrower_personal","type":"string","minLength":1,"maxLength":80},"borrower_email":{"type":"string","maxLength":254,"description":"An email address of at most 254 characters, with no spaces, a single `@`, and a domain with a dot that has text before it and at least two characters after it (`example.com`). Spaces around it are ignored. Stored in lowercase. At least one of `borrower_email` and `borrower_phone` is required. An invitation referral (the default `consent_mode`) needs an email while text invitations are off for its loss state: one without it is refused with 422 `email_required_sms_disabled`. A warm-handoff referral may be phone-only.","x-data-class":"borrower_contact","nullable":true,"example":"jordan.reyes@example.com"},"borrower_phone":{"type":"string","description":"A 10-digit US number; punctuation and a leading 1 are tolerated, and it is stored as 10 digits.","x-data-class":"borrower_contact","nullable":true,"example":"(512) 555-0142"},"vin":{"type":"string","description":"11 to 17 letters and digits, never I, O or Q. Case-insensitive; stored in uppercase.","x-data-class":"borrower_personal","nullable":true,"example":"1FTFW1ET5DFC10312"},"vehicle_year":{"type":"integer","minimum":1950,"description":"A model year from 1950 to two years after the current year.","x-data-class":"borrower_personal","nullable":true,"example":2021},"vehicle_make":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"vehicle_model":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"primary_carrier":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","maxLength":120,"x-data-class":"borrower_personal","nullable":true},"loss_state":{"type":"string","description":"The two-letter state of the loss, case-insensitive (`tx` is `TX`).","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"garaged_state":{"type":"string","description":"The two-letter USPS code of the state where the vehicle is garaged (a state or DC), case-insensitive. We judge this state when it is sent, else `loss_state`: a referral for a state we don't serve may be refused with 422 `state_not_served`. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"borrower_personal","nullable":true,"example":"TX"},"external_ref":{"type":"string","maxLength":120,"description":"Your own opaque reference, such as a case or file ID from your system; echoed in responses and webhooks. Never a loan, account or policy number (a credit union member number is an account number).","x-data-class":"institution_confidential","nullable":true,"example":"CASE-4471"},"initial_offer_cents":{"type":"integer","minimum":0,"maximum":1000000000,"description":"The insurer's initial ACV offer, in cents.","x-data-class":"borrower_personal","nullable":true},"program":{"description":"New program terms. Locked once the borrower activates under them (409 `economics_locked`); switching into provider-paid or split-pay needs a billing method (403 `billing_required`). Null is the same as leaving `program` out: the terms stay as they are.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/ReferralProgramEditInput"},{"type":"object","nullable":true,"enum":[null]}]},"claim_against":{"type":"string","enum":["own","other_driver","unknown",null],"description":"Whose insurer is handling the claim: `own` (the borrower's own insurer), `other_driver` (another driver's insurer), or `unknown`. Case-insensitive. Leave it out when you don't know. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"borrower_personal","nullable":true},"cause_of_loss":{"type":"string","enum":["collision","theft","fire","flood","weather","vandalism","animal","other",null],"description":"The cause of the loss, if you know it. `weather` is hail, wind or a storm; a flood is `flood`. Case-insensitive. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"borrower_personal","nullable":true},"trigger":{"type":"string","enum":["payoff_request","loss_notice","gap_claim","borrower_request","other",null],"description":"What led you to refer: `payoff_request` (an insurer asked you for the loan payoff), `loss_notice` (a notice of loss), `gap_claim` (the borrower filed a GAP claim), `borrower_request` (the borrower asked), or `other`. Case-insensitive. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"institution_confidential","nullable":true},"payoff_requested_at":{"type":"string","description":"When the insurer asked you for the loan payoff: a date (`2026-06-16`, read as midnight UTC) or an ISO 8601 timestamp with its UTC offset (`2026-06-16T15:04:00-06:00`). At most a day ahead. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"institution_confidential","nullable":true,"example":"2026-06-16T15:04:00-06:00"},"payoff_request_channel":{"type":"string","enum":["phone","fax","email","mail","web_portal","electronic_service","other",null],"description":"How that payoff request reached you. `web_portal` is your own online portal; `electronic_service` is a third-party payoff or letter-of-guarantee service the insurer used. Case-insensitive. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"institution_confidential","nullable":true},"client_name":{"type":"string","maxLength":120,"description":"For a GAP administrator: the lender or dealer whose borrower this is. At most 120 characters. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"institution_confidential","nullable":true},"requirement_basis":{"description":"Send it only when the borrower's contract lets you require the review. It is recorded and checked against your program's bases; messages to the borrower still ask rather than require. Editable until the borrower activates; null or \"\" clears it.","x-data-class":"borrower_personal","anyOf":[{"$ref":"#/components/schemas/RequirementBasisInput"},{"type":"object","nullable":true,"enum":[null]}]},"action":{"description":"Null is the same as leaving `action` out. To cancel, send `{ \"action\": \"cancel\" }` on its own.","x-data-class":"public","type":"string","nullable":true,"enum":[null]}},"additionalProperties":false},"RequirementBasisInput":{"description":"The contract basis for requiring the review. Every member is required except `filed_form_ref`, which a `carrier` basis needs.","type":"object","properties":{"basis_kind":{"description":"`contract`: a provision in the borrower's own contract. `carrier`: a GAP insurance policy's form, filed in the state, which needs `filed_form_ref`. Case-insensitive.","x-data-class":"institution_confidential","type":"string","enum":["contract","carrier"]},"contract_form_id":{"description":"The contract form's identifier, as the form names itself. 1 to 64 characters: letters, digits, spaces and . , _ - / ( ) # &. Spaces around it are ignored.","x-data-class":"institution_confidential","type":"string"},"contract_form_version":{"description":"The form's version or edition: 1 to 64 characters, in the same characters as `contract_form_id`. Spaces around it are ignored.","x-data-class":"institution_confidential","type":"string"},"contract_date":{"description":"The date of the borrower's contract (YYYY-MM-DD); at most a day ahead. Spaces around it are ignored.","format":"date","x-data-class":"borrower_personal","type":"string","example":"2025-03-01"},"product_type":{"description":"The product the contract is: a GAP waiver, GAP insurance, or a loan without GAP. Case-insensitive.","x-data-class":"institution_confidential","type":"string","enum":["gap_waiver","gap_insurance","loan_without_gap"]},"state":{"description":"The two-letter USPS code of the state whose law the contract follows (a state or DC), case-insensitive.","x-data-class":"borrower_personal","type":"string","example":"TX"},"a2_acknowledged":{"description":"Whether the borrower acknowledged the signing disclosure in its own box (the program paper's Appendix A-2).","x-data-class":"borrower_personal","type":"boolean"},"adopts_x5":{"description":"Whether the contract adopts X.5 (no delay; no charges).","x-data-class":"institution_confidential","type":"boolean"},"adopts_x6":{"description":"Whether the contract adopts X.6 (no worse off).","x-data-class":"institution_confidential","type":"boolean"},"filed_form_ref":{"type":"string","description":"A `carrier` basis's filed form: its filing reference, as an identifier of at most 64 letters, digits, dots, hyphens and underscores. Never a URL. Spaces around it are ignored.","x-data-class":"institution_confidential","nullable":true,"example":"SERFF.TX-2024_0012"}},"required":["basis_kind","contract_form_id","contract_form_version","contract_date","product_type","state","a2_acknowledged","adopts_x5","adopts_x6"],"additionalProperties":false},"ReviewRecord":{"description":"One version of a referral's review record. Versions only grow: one is written each time the record changes, never edited afterwards, and cited by `{ id, version, hash }`.","type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["gap.review_record"]},"id":{"description":"The version's id.","x-data-class":"institution_confidential","type":"string"},"referral_id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"version":{"description":"The version's number: 1 for the record's first version, and one more for each after it.","x-data-class":"public","type":"integer","minimum":1},"hash":{"description":"SHA-256, in lowercase hex, of `bundle`'s canonical JSON (RFC 8785): hash the bundle you read to check it. A version never changes, and a redacted one keeps its hash.","x-data-class":"institution_confidential","type":"string","pattern":"^[0-9a-f]{64}$"},"status":{"description":"The review's status as of this version. `PENDING`: referred, and the borrower hasn't engaged yet. `IN_REVIEW`: the borrower activated, and no review has reached them. `REVIEWED_NO_UNDERVALUATION`: we told the borrower the offer looks fair. `REVIEWED_UNDERVALUATION_FOUND`: we told the borrower we can help. `RESEARCH_DELIVERED`: our research reached the borrower, with no verdict. `REVIEWED_ELSEWHERE`: the borrower had the review done elsewhere. `DECLINED`: the borrower declined. `UNREACHABLE`: we couldn't reach the borrower, or the invitation expired. `RELEASED`: the review window ended with nothing delivered. `CANCELLED`: you cancelled the referral before the borrower activated. `REPORTING_WITHDRAWN`: the borrower asked us to stop reporting this referral's progress. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","IN_REVIEW","REVIEWED_NO_UNDERVALUATION","REVIEWED_UNDERVALUATION_FOUND","RESEARCH_DELIVERED","REVIEWED_ELSEWHERE","DECLINED","UNREACHABLE","RELEASED","CANCELLED","REPORTING_WITHDRAWN"]},"created_at":{"description":"When the version was written.","x-data-class":"institution_confidential","type":"string","format":"date-time"},"redacted":{"description":"True once the version's bundle has been removed: when its retention period ends, or when the borrower's data is erased at their request. Its id, version, hash and status stay.","x-data-class":"public","type":"boolean"},"bundle":{"description":"The record itself, or null when the version is redacted.","x-data-class":"borrower_personal","anyOf":[{"anyOf":[{"$ref":"#/components/schemas/ReviewRecordBundle"},{"$ref":"#/components/schemas/WithdrawnReviewRecordBundle"}]},{"type":"object","nullable":true,"enum":[null]}]}},"required":["object","id","referral_id","version","hash","status","created_at","redacted","bundle"]},"ReviewRecordBundle":{"description":"One version of a referral's review record (`review-record.v1`). It names no one and carries no contact detail, VIN, claim number or dollar range, and it shows a verdict only once the verdict reached the borrower. Every date is ISO 8601 in UTC, and every amount integer cents.","type":"object","properties":{"schema":{"description":"The bundle's format.","x-data-class":"public","type":"string","enum":["review-record.v1"]},"referral_id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"status":{"description":"The review's status as of this version, as the record's `status` gives it. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","IN_REVIEW","REVIEWED_NO_UNDERVALUATION","REVIEWED_UNDERVALUATION_FOUND","RESEARCH_DELIVERED","REVIEWED_ELSEWHERE","DECLINED","UNREACHABLE","RELEASED","CANCELLED"]},"review_completed_at":{"type":"string","format":"date-time","description":"When the review completed: when our verdict, or our paid research, first reached the borrower, or when they had the review done elsewhere. Null until then; nothing after it moves it.","x-data-class":"institution_confidential","nullable":true},"verdict":{"type":"object","properties":{"recommendation":{"type":"string","enum":["PROCEED","DECLINE",null],"description":"The verdict that reached the borrower. `PROCEED`: we told them we can help. `DECLINE`: we told them the offer looks fair. Null until one reaches them: you never hear a verdict before the borrower does. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true},"delivered_at":{"type":"string","format":"date-time","description":"When that verdict reached the borrower.","x-data-class":"institution_confidential","nullable":true},"tier":{"type":"string","enum":["PRELIMINARY","FULL",null],"description":"The review the verdict came from. `PRELIMINARY`: made before the valuation report, photos and receipts were all in. `FULL`: made with all of them. Null without a verdict. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true},"decline_reason":{"type":"string","description":"With a `DECLINE`, why, when a reason was recorded: `OFFER_FAIR`, `GAIN_BELOW_THRESHOLD`, `NO_APPRAISAL_RIGHT`, `EVIDENCE_INSUFFICIENT`, `OUT_OF_SCOPE` or `OTHER`. Null otherwise.","x-data-class":"public","nullable":true},"research_only":{"description":"True when the borrower engaged us for research only, which carries no verdict.","x-data-class":"public","type":"boolean"},"research_delivered_at":{"type":"string","format":"date-time","description":"When our paid research reached the borrower.","x-data-class":"institution_confidential","nullable":true},"full_fac_required_at":{"type":"string","format":"date-time","description":"When a full review became due: the borrower engaged us after a preliminary one.","x-data-class":"institution_confidential","nullable":true},"full_fac_completed_at":{"type":"string","format":"date-time","description":"When that full review was completed.","x-data-class":"institution_confidential","nullable":true},"re_review_requested_at":{"type":"string","format":"date-time","description":"When a change to the vehicle or the evidence sent the verdict back to be reviewed again.","x-data-class":"institution_confidential","nullable":true}},"required":["recommendation","delivered_at","tier","decline_reason","research_only","research_delivered_at","full_fac_required_at","full_fac_completed_at","re_review_requested_at"]},"materiality":{"type":"object","properties":{"rule":{"description":"The rule `we_can_help` follows: `D12` today. Other rules may be added: treat one you don't recognise as a rule you don't know, and read `we_can_help` as that rule's verdict.","x-data-class":"public","type":"string","example":"D12"},"we_can_help":{"type":"boolean","description":"Whether we can help, under that rule: true when our valuation clears the insurer's offer by enough to be worth an appraisal, false when it doesn't. Never a dollar figure. Null when it couldn't be judged (no confirmed offer, or no valid valuation range), and until a verdict reaches the borrower.","x-data-class":"borrower_personal","nullable":true},"second_review_pending":{"description":"True while a second reviewer still has to check the verdict.","x-data-class":"public","type":"boolean"}},"required":["rule","we_can_help","second_review_pending"]},"window":{"type":"object","properties":{"valuation_received_at":{"type":"string","format":"date-time","description":"When the valuation report for the review came in.","x-data-class":"institution_confidential","nullable":true},"ends_at":{"type":"string","format":"date-time","description":"When the review window ends: 11:59:59 PM Denver time on the earlier of 5 business days after the valuation report and 15 business days after the borrower was first told about the review. Null while no window runs.","x-data-class":"institution_confidential","nullable":true}},"required":["valuation_received_at","ends_at"]},"release":{"type":"object","properties":{"released_at":{"type":"string","format":"date-time","description":"When the borrower was released: the review window ended with nothing delivered. A review the borrower has started goes on.","x-data-class":"institution_confidential","nullable":true},"notice_sent_at":{"type":"string","format":"date-time","description":"When the release notice went to the borrower.","x-data-class":"institution_confidential","nullable":true}},"required":["released_at","notice_sent_at"]},"outreach":{"type":"object","properties":{"consent_mode":{"description":"How the borrower came to us: `INVITATION` (we invited them) or `WARM_HANDOFF` (your staff handed them to us). New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"institution_confidential","type":"string","enum":["INVITATION","WARM_HANDOFF"]},"invited_at":{"type":"string","format":"date-time","description":"When we sent the invitation.","x-data-class":"institution_confidential","nullable":true},"disclosure_confirmed_at":{"type":"string","format":"date-time","description":"When your staff attested to the warm-handoff disclosure.","x-data-class":"institution_confidential","nullable":true},"first_contact_at":{"type":"string","format":"date-time","description":"When we first contacted the borrower.","x-data-class":"institution_confidential","nullable":true},"contact_attempts":{"description":"How many times we tried to reach the borrower.","x-data-class":"institution_confidential","type":"integer"},"last_contact_attempt_at":{"type":"string","format":"date-time","description":"When we last tried.","x-data-class":"institution_confidential","nullable":true},"reached_at":{"type":"string","format":"date-time","description":"When we reached the borrower.","x-data-class":"institution_confidential","nullable":true},"activated_at":{"type":"string","format":"date-time","description":"When the borrower activated.","x-data-class":"institution_confidential","nullable":true},"contact_stopped_at":{"type":"string","format":"date-time","description":"When the borrower asked us to stop contacting them about this referral.","x-data-class":"institution_confidential","nullable":true}},"required":["consent_mode","invited_at","disclosure_confirmed_at","first_contact_at","contact_attempts","last_contact_attempt_at","reached_at","activated_at","contact_stopped_at"]},"refusal":{"type":"object","properties":{"recorded_at":{"type":"string","format":"date-time","description":"When the borrower's \"no thanks\" was recorded.","x-data-class":"institution_confidential","nullable":true},"scope":{"type":"string","description":"What the refusal covers, as a code.","x-data-class":"public","nullable":true},"method":{"type":"string","description":"How the borrower gave it, as a code.","x-data-class":"public","nullable":true},"text_version":{"type":"string","description":"The version of the refusal text the borrower heard.","x-data-class":"public","nullable":true}},"required":["recorded_at","scope","method","text_version"]},"reviewed_elsewhere_at":{"type":"string","format":"date-time","description":"When we recorded that the borrower had the review done elsewhere.","x-data-class":"institution_confidential","nullable":true},"appraisal_right":{"type":"object","properties":{"claim_against":{"type":"string","description":"Whose insurer is handling the claim: `own`, `other_driver` or `unknown`, as you or the borrower told us.","x-data-class":"borrower_personal","nullable":true},"clause_invoked_at":{"type":"string","format":"date-time","description":"When the policy's appraisal clause was invoked with the insurer.","x-data-class":"institution_confidential","nullable":true},"clause_invoked_via":{"type":"string","description":"How it was invoked, as a code.","x-data-class":"public","nullable":true}},"required":["claim_against","clause_invoked_at","clause_invoked_via"]},"costs":{"type":"object","properties":{"engagement":{"type":"object","properties":{"basis":{"description":"`SNAPSHOT`: the funding terms fixed when the borrower activated. `LEGACY`: the program's original prices, for a referral without them. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"institution_confidential","type":"string","enum":["SNAPSHOT","LEGACY"]},"payer":{"description":"Who pays the engagement fee: `PARTNER` (you), `BORROWER`, `SPLIT` (both), `MEMBERSHIP` (a membership covers it) or `NONE` (there is no fee). New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"institution_confidential","type":"string","enum":["PARTNER","BORROWER","SPLIT","MEMBERSHIP","NONE"]},"list_cents":{"description":"The engagement fee before anyone's part, in cents.","x-data-class":"institution_confidential","type":"integer"},"institution_cents":{"description":"Your part of it, in cents.","x-data-class":"institution_confidential","type":"integer"},"borrower_cents":{"description":"What the borrower is charged for it, in cents, after any discount.","x-data-class":"institution_confidential","type":"integer"}},"required":["basis","payer","list_cents","institution_cents","borrower_cents"],"description":"The engagement fee: who pays it, and each part. Null for a key without `reporting:read`.","x-data-class":"institution_confidential","x-requires-permission":"reporting:read","nullable":true},"institution_charges":{"type":"array","items":{"type":"object","properties":{"kind":{"description":"The charges' kind.","x-data-class":"institution_confidential","type":"string"},"status":{"description":"Their status.","x-data-class":"institution_confidential","type":"string"},"amount_cents":{"description":"Their total, in cents.","x-data-class":"institution_confidential","type":"integer"},"count":{"description":"How many there are.","x-data-class":"institution_confidential","type":"integer"}},"required":["kind","status","amount_cents","count"]},"description":"What we have charged you on this referral, summed by kind and status. Null for a key without `reporting:read`.","x-data-class":"institution_confidential","x-requires-permission":"reporting:read","nullable":true}},"required":["engagement","institution_charges"]},"requirement":{"type":"object","properties":{"mode":{"description":"The review requirement your program had when the borrower activated: `REQUEST` (also when none was set), `REVIEW` or `REVIEW_AND_APPRAISAL`. Messages to the borrower ask rather than require.","x-data-class":"institution_confidential","type":"string"},"basis_attested":{"description":"Whether you sent a `requirement_basis` with the referral.","x-data-class":"institution_confidential","type":"boolean"},"basis_validated_at":{"type":"string","format":"date-time","description":"When that basis matched one of your program's approved bases.","x-data-class":"institution_confidential","nullable":true}},"required":["mode","basis_attested","basis_validated_at"]},"outcome":{"type":"object","properties":{"settled_at":{"type":"string","format":"date-time","description":"When the borrower's claim settled.","x-data-class":"institution_confidential","nullable":true},"closed_at":{"type":"string","format":"date-time","description":"When the referral's record closed.","x-data-class":"institution_confidential","nullable":true},"close_reason":{"type":"string","enum":["SETTLED","NO_MATERIAL_UNDERVALUATION","BORROWER_DECLINED","BORROWER_UNREACHABLE","INVITATION_EXPIRED","CANCELLED_BY_INSTITUTION","REVIEWED_ELSEWHERE","SETTLED_WITH_INSURER","OUT_OF_SCOPE","STATE_NOT_SERVED","NO_APPRAISAL_RIGHT","CLAIM_NOT_COVERED","NOT_COMPLETED","REPORTING_WITHDRAWN",null],"description":"Why it closed: null while it is open. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true}},"required":["settled_at","closed_at","close_reason"]},"copy_versions":{"type":"object","properties":{"disclosure_script":{"type":"string","description":"The version of the warm-handoff disclosure script your staff delivered.","x-data-class":"public","nullable":true},"refusal_text":{"type":"string","description":"The version of the refusal text the borrower heard.","x-data-class":"public","nullable":true}},"required":["disclosure_script","refusal_text"]}},"required":["schema","referral_id","status","review_completed_at","verdict","materiality","window","release","outreach","refusal","reviewed_elsewhere_at","appraisal_right","costs","requirement","outcome","copy_versions"]},"ReviewRecordReference":{"description":"The latest version of the referral's review record, cited as the review webhooks cite one.","type":"object","properties":{"id":{"description":"The version's id.","x-data-class":"institution_confidential","type":"string"},"version":{"description":"The version's number: 1 for the record's first version, and one more for each after it.","x-data-class":"public","type":"integer","minimum":1},"hash":{"description":"SHA-256, in lowercase hex, of the version's canonical JSON (RFC 8785). A version never changes, so the version you read later still matches it.","x-data-class":"institution_confidential","type":"string","pattern":"^[0-9a-f]{64}$"},"status":{"description":"The review's status as of this version. `PENDING`: referred, and the borrower hasn't engaged yet. `IN_REVIEW`: the borrower activated, and no review has reached them. `REVIEWED_NO_UNDERVALUATION`: we told the borrower the offer looks fair. `REVIEWED_UNDERVALUATION_FOUND`: we told the borrower we can help. `RESEARCH_DELIVERED`: our research reached the borrower, with no verdict. `REVIEWED_ELSEWHERE`: the borrower had the review done elsewhere. `DECLINED`: the borrower declined. `UNREACHABLE`: we couldn't reach the borrower, or the invitation expired. `RELEASED`: the review window ended with nothing delivered. `CANCELLED`: you cancelled the referral before the borrower activated. `REPORTING_WITHDRAWN`: the borrower asked us to stop reporting this referral's progress. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","IN_REVIEW","REVIEWED_NO_UNDERVALUATION","REVIEWED_UNDERVALUATION_FOUND","RESEARCH_DELIVERED","REVIEWED_ELSEWHERE","DECLINED","UNREACHABLE","RELEASED","CANCELLED","REPORTING_WITHDRAWN"]},"created_at":{"description":"When the version was written.","x-data-class":"institution_confidential","type":"string","format":"date-time"}},"required":["id","version","hash","status","created_at"]},"ReviewStatus":{"description":"One of your referrals the lookup matched, and its review status: the version its record stands at.","type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["gap.review_status"]},"referral":{"type":"object","properties":{"id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"external_ref":{"type":"string","description":"Your own reference, as you sent it.","x-data-class":"institution_confidential","nullable":true}},"required":["id","external_ref"]},"record":{"description":"The latest version of the referral's review record. Null while it has none to show: before its first version is written, and from the borrower's request that we stop reporting the referral's progress until the version that records it.","x-data-class":"institution_confidential","anyOf":[{"$ref":"#/components/schemas/ReviewRecordReference"},{"type":"object","nullable":true,"enum":[null]}]},"close_reason":{"type":"string","enum":["SETTLED","NO_MATERIAL_UNDERVALUATION","BORROWER_DECLINED","BORROWER_UNREACHABLE","INVITATION_EXPIRED","CANCELLED_BY_INSTITUTION","REVIEWED_ELSEWHERE","SETTLED_WITH_INSURER","OUT_OF_SCOPE","STATE_NOT_SERVED","NO_APPRAISAL_RIGHT","CLAIM_NOT_COVERED","NOT_COMPLETED","REPORTING_WITHDRAWN",null],"description":"Why the referral's record closed, as of that version: null while it is open, and whenever `record` is null. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true}},"required":["object","referral","record","close_reason"]},"ReviewStatusList":{"description":"Your referrals the identifier matched, newest first, at most 100; an empty list when none did.","type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReviewStatus"}},"has_more":{"description":"True when more than 100 of your referrals matched: the newest 100 are listed.","x-data-class":"public","type":"boolean"}},"required":["object","data","has_more"]},"ReviewStatusLookupInput":{"description":"Exactly one identifier, matched against your referrals: `vin`, or `claim_number` or `external_ref` as you sent it on the referral, spaces around it aside. One that is blank or null is not sent.","type":"object","properties":{"vin":{"type":"string","description":"The vehicle's VIN: 11 to 17 letters and digits, never I, O or Q. Case-insensitive.","x-data-class":"borrower_personal","nullable":true,"example":"1FTFW1ET5DFC10312"},"claim_number":{"type":"string","maxLength":120,"description":"The borrower's claim number with their insurer, as you sent it on the referral.","x-data-class":"borrower_personal","nullable":true},"external_ref":{"type":"string","maxLength":120,"description":"Your own reference for the referral, as you sent it.","x-data-class":"institution_confidential","nullable":true}},"additionalProperties":false},"SignatureCheckResult":{"description":"What a test credential's signed request looked like to the API: whether it verified, and the base it was checked over.","type":"object","properties":{"object":{"x-data-class":"public","type":"string","enum":["gap.signature_check"]},"verified":{"description":"Whether the request's signature verified with the credential's key over the signature base built from the request as received.","x-data-class":"public","type":"boolean"},"signature_base":{"description":"The signature base built from the request as received (RFC 9421 section 2.5), to compare byte for byte with the one you signed.","x-data-class":"institution_confidential","type":"string"},"code":{"description":"When `verified` is false: the code the same request would be refused with on any other operation. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["signature_profile_invalid","signature_expired","target_uri_not_allowed","signature_invalid","environment_mismatch"]}},"required":["object","verified"]},"ThinEvent":{"description":"One event, thin: what happened, to which record, and when. It is what the events feed lists and what a thin-payload webhook endpoint is sent (one created from the partner security platform's `issue` stage on), with the webhook's headers. New event types are added over time: a feed reader skips a type it doesn't recognise, and a webhook endpoint answers 2xx to a type it doesn't recognise, and ignores it.","type":"object","properties":{"id":{"description":"The event id (`evt_…`), the same id its webhook delivery carries: use it to drop duplicates across the feed and your webhooks. It is not a position: page with `next_cursor`.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.invited","referral.activated","referral.declined","consultation.status_changed","appraisal.completed","settlement.updated","charge.created","plan.enrollment.activated","plan.enrollment.past_due","plan.enrollment.cancelled","plan.benefit.redeemed","plan.benefit.completed","plan.statement.issued","review.completed","review.updated","referral.closed"],"description":"New values may be added: ignore a type you don't recognise."},"api_version":{"description":"The thin event's version: `1.1`.","x-data-class":"public","type":"string","enum":["1.1"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one; a membership plan event has its enrollment's. A membership statement and its charge don't carry it: a live key lists them, and they go to your live endpoints.","x-data-class":"public","type":"boolean"},"account":{"description":"The institution the event belongs to.","type":"object","properties":{"id":{"description":"Your institution's id.","x-data-class":"institution_confidential","type":"string"}},"required":["id"]},"data":{"description":"The thin body, by `type`: the referral's `id`, `status`, your `external_ref` and its timestamps under `referral`, or the enrollment's under `enrollment`; the milestone's `key`, `phase`, `previous` and `completed` list; the codes and ids the event adds (`channel`, `via`, `consultation_number`, `reason`, `redemption_id`); a charge's `id`, `status` and `statement_id`; a statement's `id`, period, `status` and times; a review event's `record` (`id`, `version`, `hash`, `status`) and `close_reason`. Never a borrower's contact details, a payoff, a VIN or an amount: fetch the referral or enrollment for those. On the events feed, a key without `referrals:read` gets the referral as its `id` only, so a `charge.created` it lists names its referral but not your reference, its status or its timestamps; a thin-payload webhook endpoint is sent the whole thin body. After an erasure an event carries only the id and `scrubbed: true`.","x-data-class":"institution_confidential","type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","api_version","created_at","account","data"]},"TimelineMilestone":{"type":"object","properties":{"key":{"x-data-class":"public","type":"string","enum":["referral_submitted","borrower_invited","disclosure_attested","outreach_in_progress","borrower_activated","valuation_report_received","insurance_details_received","photos_received","market_research","preliminary_estimate_ready","appraisal_report_complete","authorization_received","insurer_notified","insurer_acknowledged","insurer_appraiser_appointed","appraisals_exchanged","umpire_engaged","award_letter_issued","settlement_finalized","outcome_recorded"],"description":"New values may be added: show one you don't recognise as not yet known, and don't fail."},"label":{"x-data-class":"public","type":"string"},"detail":{"x-data-class":"public","type":"string"},"state":{"x-data-class":"public","type":"string","enum":["complete","current","upcoming","skipped"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"at":{"type":"string","format":"date-time","description":"When the milestone completed, when known.","x-data-class":"institution_confidential","nullable":true},"waiting_on":{"type":"string","enum":["borrower","secondappraisal","insurer",null],"description":"Who the current milestone waits on. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true},"waiting_label":{"type":"string","x-data-class":"public","nullable":true},"delayed":{"description":"True when the current milestone has run past its expected time.","x-data-class":"public","type":"boolean"},"note":{"type":"string","description":"A third-person fact line, such as the number of contact attempts.","x-data-class":"institution_confidential","nullable":true}},"required":["key","label","detail","state","at","waiting_on","waiting_label","delayed","note"]},"WebhookEvent":{"description":"The full payload: the envelope a full-payload endpoint, one created before the partner security platform's `issue` stage, is sent; a thin-payload endpoint, created from `issue` on, is sent `ThinEvent` instead, with the same headers. Every delivery is a POST. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise. Verify `X-SecondAppraisal-Signature: t=<unix seconds>,v1=<hex>` first: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`; compare in constant time and refuse an old `t`. The key is the secret's text for a `whsec_gap_` secret, and the base64-decoded bytes after `whsec_` for a Standard Webhooks secret, whose endpoint also gets `webhook-id`, `webhook-timestamp` and `webhook-signature`, which any Standard Webhooks library verifies. Answer any 2xx to acknowledge; anything else is retried with backoff. New event types are added over time: answer 2xx to a type you don't recognise, and ignore it.","type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.invited","referral.activated","referral.declined","consultation.status_changed","appraisal.completed","settlement.updated","charge.created","plan.enrollment.activated","plan.enrollment.past_due","plan.enrollment.cancelled","plan.benefit.redeemed","plan.benefit.completed","plan.statement.issued","review.completed","review.updated","referral.closed"],"description":"New values may be added: ignore a type you don't recognise."},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"Depends on `type`.","x-data-class":"borrower_contact","type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},"WebhookReferral":{"description":"A referral as a webhook carries it. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise. The API's referral carries more: fetch it to read every field. Once the borrower withdraws permission to report the referral's progress, it carries `status: reporting_withdrawn` and no progress, as every read shows it.","type":"object","properties":{"id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["gap.referral"]},"status":{"description":"Where the referral stands. `reporting_withdrawn` once the borrower withdrew permission to report its progress to you, whatever happens to it after: from then on it shows the fields you supplied and no progress. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["submitted","invited","handoff_pending","outreach_queued","contact_attempted","activated","in_progress","settled","closed","declined","unreachable","expired","cancelled","reporting_withdrawn"]},"status_label":{"description":"The status as the portal shows it.","x-data-class":"public","type":"string"},"status_tone":{"x-data-class":"public","type":"string","enum":["green","yellow","red","neutral"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"status_note":{"type":"string","description":"A third-person note on a terminal status, such as why it closed.","x-data-class":"institution_confidential","nullable":true},"external_ref":{"type":"string","description":"Your own reference, as you sent it.","x-data-class":"institution_confidential","nullable":true},"borrower":{"type":"object","properties":{"first_name":{"x-data-class":"borrower_personal","type":"string"},"last_name":{"x-data-class":"borrower_personal","type":"string"},"email":{"type":"string","x-data-class":"borrower_contact","nullable":true},"phone":{"type":"string","description":"10 digits.","x-data-class":"borrower_contact","nullable":true}},"required":["first_name","last_name","email","phone"]},"vehicle":{"type":"object","properties":{"vin":{"type":"string","x-data-class":"borrower_personal","nullable":true},"year":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"make":{"type":"string","x-data-class":"borrower_personal","nullable":true},"model":{"type":"string","x-data-class":"borrower_personal","nullable":true}},"required":["vin","year","make","model"]},"claim":{"type":"object","properties":{"carrier":{"type":"string","x-data-class":"borrower_personal","nullable":true},"claim_number":{"type":"string","x-data-class":"borrower_personal","nullable":true},"loss_state":{"type":"string","x-data-class":"borrower_personal","nullable":true},"date_of_loss":{"type":"string","format":"date-time","description":"Midnight UTC on the date of loss.","x-data-class":"borrower_personal","nullable":true},"initial_offer_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true}},"required":["carrier","claim_number","loss_state","date_of_loss","initial_offer_cents"]},"liability":{"type":"object","properties":{"loan_payoff_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"deductible_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true}},"required":["loan_payoff_cents","deductible_cents"]},"program":{"type":"object","properties":{"mode":{"x-data-class":"institution_confidential","type":"string","enum":["provider_paid","split_pay","customer_paid","membership"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"subsidy_type":{"type":"string","enum":["percent","fixed_cents",null],"x-data-class":"institution_confidential","description":"New values may be added: treat one you don't recognise as unknown, and don't fail.","nullable":true},"subsidy_value":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"price_cents":{"description":"Your price for this referral's consultation, in cents, fixed when the referral was submitted. `mode` and the subsidy terms decide how much of it you pay.","x-data-class":"institution_confidential","type":"integer"},"locked":{"type":"boolean","description":"True once the borrower activated under these terms; they can no longer change. Null once reporting is withdrawn (`status: reporting_withdrawn`).","x-data-class":"institution_confidential","nullable":true}},"required":["mode","subsidy_type","subsidy_value","price_cents","locked"]},"consent":{"type":"object","properties":{"mode":{"x-data-class":"institution_confidential","type":"string","enum":["invitation","warm_handoff"],"description":"New values may be added: treat one you don't recognise as unknown, and don't fail."},"disclosure_confirmed_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"disclosure_channel":{"type":"string","x-data-class":"institution_confidential","nullable":true},"disclosure_attestor_name":{"type":"string","x-data-class":"institution_confidential","nullable":true}},"required":["mode","disclosure_confirmed_at","disclosure_channel","disclosure_attestor_name"]},"submitted_via":{"description":"Where the referral came from: `api`, `dashboard` or `csv`.","x-data-class":"public","type":"string"},"created_at":{"x-data-class":"institution_confidential","type":"string","format":"date-time"},"invited_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"invitation_expires_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"activated_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"settled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true},"closed_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true}},"required":["id","object","status","status_label","status_tone","status_note","external_ref","borrower","vehicle","claim","liability","program","consent","submitted_via","created_at","invited_at","invitation_expires_at","activated_at","settled_at","closed_at"]},"WebhookReferralActivatedData":{"description":"The referral, its consultation number, and how the borrower activated. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","type":"object","properties":{"referral":{"$ref":"#/components/schemas/WebhookReferral"},"consultation_number":{"type":"string","description":"Our consultation number for the borrower's case.","x-data-class":"institution_confidential","nullable":true},"via":{"description":"Whether the borrower activated through the link or with our team's help. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["borrower_link","admin_assisted"]}},"required":["referral","consultation_number","via"]},"WebhookReferralData":{"description":"The referral, as webhooks carry it. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","type":"object","properties":{"referral":{"$ref":"#/components/schemas/WebhookReferral"}},"required":["referral"]},"WebhookReferralInvitedData":{"description":"The referral, and how its invitation went out. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","type":"object","properties":{"referral":{"$ref":"#/components/schemas/WebhookReferral"},"channel":{"description":"How the invitation went out. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","type":"string","enum":["email","sms"]}},"required":["referral","channel"]},"WebhookReviewRecord":{"description":"The version of the referral's review record that the event reports.","type":"object","properties":{"id":{"description":"The version's id.","x-data-class":"institution_confidential","type":"string"},"version":{"description":"The version's number: 1 for the record's first version, and one more for each after it.","x-data-class":"public","type":"integer","minimum":1},"hash":{"description":"SHA-256, in lowercase hex, of the version's canonical JSON (RFC 8785). A version never changes, so the version you read later still matches it.","x-data-class":"institution_confidential","type":"string","pattern":"^[0-9a-f]{64}$"},"status":{"description":"The review's status as of this version. `PENDING`: referred, and the borrower hasn't engaged yet. `IN_REVIEW`: the borrower activated, and no review has reached them. `REVIEWED_NO_UNDERVALUATION`: we told the borrower the offer looks fair. `REVIEWED_UNDERVALUATION_FOUND`: we told the borrower we can help. `RESEARCH_DELIVERED`: our research reached the borrower, with no verdict. `REVIEWED_ELSEWHERE`: the borrower had the review done elsewhere. `DECLINED`: the borrower declined. `UNREACHABLE`: we couldn't reach the borrower, or the invitation expired. `RELEASED`: the review window ended with nothing delivered. `CANCELLED`: you cancelled the referral before the borrower activated. `REPORTING_WITHDRAWN`: the borrower asked us to stop reporting this referral's progress. New values may be added: show one you don't recognise as not yet known, and don't fail.","x-data-class":"public","type":"string","enum":["PENDING","IN_REVIEW","REVIEWED_NO_UNDERVALUATION","REVIEWED_UNDERVALUATION_FOUND","RESEARCH_DELIVERED","REVIEWED_ELSEWHERE","DECLINED","UNREACHABLE","RELEASED","CANCELLED","REPORTING_WITHDRAWN"]}},"required":["id","version","hash","status"]},"WebhookReviewRecordData":{"description":"The referral, and the version of its review record that the event reports. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise. A version that completes the review and closes the referral sends both review.completed and referral.closed. Staged: none of these events is sent until review records open.","type":"object","properties":{"referral":{"type":"object","properties":{"id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"external_ref":{"type":"string","description":"Your own reference, as you sent it.","x-data-class":"institution_confidential","nullable":true}},"required":["id","external_ref"]},"record":{"$ref":"#/components/schemas/WebhookReviewRecord"},"close_reason":{"type":"string","enum":["SETTLED","NO_MATERIAL_UNDERVALUATION","BORROWER_DECLINED","BORROWER_UNREACHABLE","INVITATION_EXPIRED","CANCELLED_BY_INSTITUTION","REVIEWED_ELSEWHERE","SETTLED_WITH_INSURER","OUT_OF_SCOPE","STATE_NOT_SERVED","NO_APPRAISAL_RIGHT","CLAIM_NOT_COVERED","NOT_COMPLETED","REPORTING_WITHDRAWN",null],"description":"Why the referral's record closed: null while it is open, and again if it reopens. New values may be added: treat one you don't recognise as unknown, and don't fail.","x-data-class":"public","nullable":true}},"required":["referral","record","close_reason"]},"WebhookSettlementData":{"description":"The referral, and the outcome its settlement recorded. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","type":"object","properties":{"referral":{"$ref":"#/components/schemas/WebhookReferral"},"outcome":{"type":"object","properties":{"initial_acv_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"appraised_value_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"final_settlement_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"uplift_cents":{"type":"integer","x-data-class":"borrower_personal","nullable":true},"exposure_before_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"exposure_after_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"fees_paid_cents":{"type":"integer","x-data-class":"institution_confidential","nullable":true},"settled_at":{"type":"string","format":"date-time","x-data-class":"institution_confidential","nullable":true}},"required":["initial_acv_cents","appraised_value_cents","final_settlement_cents","uplift_cents","exposure_before_cents","exposure_after_cents","fees_paid_cents","settled_at"]}},"required":["referral","outcome"]},"WithdrawnReviewRecordBundle":{"description":"A version written after the borrower asked us to stop reporting the referral's progress: it says only that, and when. The versions before it stay readable by number.","type":"object","properties":{"schema":{"description":"The bundle's format.","x-data-class":"public","type":"string","enum":["review-record.v1"]},"referral_id":{"description":"The referral's id.","x-data-class":"institution_confidential","type":"string"},"status":{"x-data-class":"public","type":"string","enum":["REPORTING_WITHDRAWN"]},"close_reason":{"x-data-class":"public","type":"string","enum":["REPORTING_WITHDRAWN"]},"reporting_withdrawn_at":{"description":"When the borrower asked us to stop reporting this referral's progress to you.","x-data-class":"institution_confidential","type":"string","format":"date-time"}},"required":["schema","referral_id","status","close_reason","reporting_withdrawn_at"]}}},"x-error-catalog":{"invalid_json":{"status":400,"title":"Invalid JSON","description":"The body is missing, or is not valid JSON in UTF-8.","type":"https://secondappraisal.com/developers/errors/invalid_json"},"unsupported_media_type":{"status":415,"title":"Unsupported media type","description":"A request body must be sent with `Content-Type: application/json`.","type":"https://secondappraisal.com/developers/errors/unsupported_media_type"},"payload_too_large":{"status":413,"title":"Payload too large","description":"The body is larger than the operation accepts: 65,536 bytes for one object, 1,048,576 bytes for a bulk call. Split the batch.","type":"https://secondappraisal.com/developers/errors/payload_too_large"},"too_many_rows":{"status":413,"title":"Too many rows","description":"A bulk call carries at most 500 rows. Split the batch.","type":"https://secondappraisal.com/developers/errors/too_many_rows"},"prohibited_field":{"status":400,"title":"Prohibited field","description":"The body names a field a referral must never carry: an SSN or tax id, a date of birth, an account, loan, member, card or routing number, a credit score or report, income or salary (matched on the field's name, at the top level and inside `program` and `disclosure`). `field_errors` names each one. Nothing else in the body is checked until they are gone, nothing is written, and the values are never stored, logged or sent back. Remove the fields and send the request again.","type":"https://secondappraisal.com/developers/errors/prohibited_field"},"validation_failed":{"status":400,"title":"Validation failed","description":"One or more fields, query parameters or path parameters failed validation. `field_errors` names each one and why.","type":"https://secondappraisal.com/developers/errors/validation_failed"},"operation_not_open":{"status":404,"title":"Operation not open yet","description":"The operation is staged: documented, and served once the switch its `x-availability.opens_with` names opens (`referral_sandbox`: when the referral sandbox opens; `partner_events`: when the partner security platform opens the events feed; `signed_credentials`: when signed credentials open; `review_record`: when review records open). Until then every call answers 404 with this code, before the key is checked, and nothing is written. Retry once it is open; until then, use the operations that are available.","type":"https://secondappraisal.com/developers/errors/operation_not_open"},"idempotency_key_too_long":{"status":400,"title":"Idempotency-Key too long","description":"The `Idempotency-Key` header is at most 255 characters (251 on a bulk enrollment call, which appends each row's index). Longer keys are refused rather than cut, so two keys can never collide.","type":"https://secondappraisal.com/developers/errors/idempotency_key_too_long"},"internal_error":{"status":500,"title":"Internal error","description":"Something failed on our side. A retry with the same Idempotency-Key is safe; if it persists, send us the `request_id`.","type":"https://secondappraisal.com/developers/errors/internal_error"},"missing_api_key":{"status":401,"title":"Missing API key","description":"Send your API key as `Authorization: Bearer <key>`: a v2 key (`sa_live_...` or `sa_test_...`) or a legacy key (`sa_gap_...`). A v2 key whose checksum doesn't match, because it was mistyped or cut short, is refused this way too, before any lookup.","type":"https://secondappraisal.com/developers/errors/missing_api_key"},"invalid_api_key":{"status":401,"title":"Invalid API key","description":"The key is not one we issued, or it was revoked.","type":"https://secondappraisal.com/developers/errors/invalid_api_key"},"api_key_expired":{"status":401,"title":"API key expired","description":"The key is past its expiry. A test key (`sa_gap_test_...` or `sa_test_...`) expires at most 90 days after it is created, a v2 live key (`sa_live_...`) at most 365 days after, and a legacy live key (`sa_gap_...`) only when we schedule its retirement, with notice. Create a new key in the portal. A signed credential expires as a v2 key does, counted from its registration, and is answered this way only once its signature verifies.","type":"https://secondappraisal.com/developers/errors/api_key_expired"},"key_not_scoped":{"status":403,"title":"Key not scoped for this surface","description":"A legacy key's (`sa_gap_...`) scopes do not include this surface: `referrals` covers the referral and analytics operations, `plans` the membership plan operations, and `read` the plan list operations and analytics. A key with no scopes reaches every surface. A v2 key is refused with `permission_denied` instead.","type":"https://secondappraisal.com/developers/errors/key_not_scoped"},"test_key_referrals_unavailable":{"status":403,"title":"Test keys can't reach referrals yet","description":"Test keys (`sa_gap_test_...` or `sa_test_...`) can't reach the referral and analytics operations until the referral sandbox opens; until then they work only with the membership plan operations, so use a live key for referral and analytics calls. Once it opens, a test key reads and writes sandbox referrals only, which reach no borrower and are never billed, and a live key never sees them.","type":"https://secondappraisal.com/developers/errors/test_key_referrals_unavailable"},"provider_terminated":{"status":403,"title":"Account terminated","description":"This institution's account has been terminated.","type":"https://secondappraisal.com/developers/errors/provider_terminated"},"provider_suspended":{"status":403,"title":"Account suspended","description":"A suspended account can read but not write. Contact the SecondAppraisal team to reinstate it.","type":"https://secondappraisal.com/developers/errors/provider_suspended"},"provider_not_active":{"status":403,"title":"Account not active","description":"An account that is not yet active can read but not write.","type":"https://secondappraisal.com/developers/errors/provider_not_active"},"rate_limited":{"status":429,"title":"Rate limit exceeded","description":"Each key may make 300 reads, 120 writes and 12 bulk calls a minute. Wait the number of seconds in `Retry-After`.","type":"https://secondappraisal.com/developers/errors/rate_limited"},"auth_unavailable":{"status":500,"title":"Key check unavailable","description":"We couldn't check the API key just now. Retry shortly.","type":"https://secondappraisal.com/developers/errors/auth_unavailable"},"permission_denied":{"status":403,"title":"Permission denied","description":"The v2 key (`sa_live_...` or `sa_test_...`) doesn't hold the permission this operation needs, which `x-required-permission` names; `error` names it too. A v2 key holds only the permissions chosen when it was created. Create a key with the permission in the portal.","type":"https://secondappraisal.com/developers/errors/permission_denied"},"credentials_unavailable":{"status":503,"title":"Credentials unavailable","description":"We couldn't check the API key or signed credential just now, so the call was refused. It wasn't counted against the key's rate limit, nothing was written to the key, and a signed request's nonce wasn't spent. Retry after the seconds in `Retry-After`; if it persists, send us the `request_id`.","type":"https://secondappraisal.com/developers/errors/credentials_unavailable"},"signed_requests_required":{"status":401,"title":"Signed requests required","description":"Your institution accepts signed requests only, so a call made with an API key (`Authorization: Bearer ...`) is refused, whatever the key may do. Sign the request with a signed credential (`signedRequest`) instead. Ask SecondAppraisal to accept API keys again if you need them.","type":"https://secondappraisal.com/developers/errors/signed_requests_required"},"ip_not_allowed":{"status":403,"title":"Address not allowed","description":"The key holds an IP allowlist, and this request came from an address outside it: the address our edge saw, never one a header names. Send the request from an address the allowlist holds, or change the key's allowlist in the portal. A request refused this way isn't counted against the key's rate limit, and a signed request's nonce isn't spent.","type":"https://secondappraisal.com/developers/errors/ip_not_allowed"},"auth_failures_throttled":{"status":429,"title":"Too many failed authentications","description":"Too many requests from your address (for IPv6, from its /64) failed authentication in the last minute, so this one is answered this way instead of with its 401: its key is missing or unknown, or it is a signed request that didn't verify or whose body doesn't match its `Content-Digest`. A signed request that doesn't verify can be, a revoked signed credential's included, since nothing about it is proven until it verifies. A bearer request whose key is valid, revoked or expired never is, unless it also carries `Signature-Input`, which makes it a signed request (401 `signature_profile_invalid` beside a bearer key); nor is a signed request that verified and whose body matched. Nor is one from an address many callers can share (a proxy or CDN range, a private network, carrier-grade NAT). Check the key or the signature, then retry after the seconds in `Retry-After`.","type":"https://secondappraisal.com/developers/errors/auth_failures_throttled"},"signature_profile_invalid":{"status":401,"title":"Signature doesn't follow the profile","description":"The signature headers don't follow the gap-v2 profile: one signature labelled `sa` in `Signature-Input` and `Signature`; covering `@method` and `@target-uri`, then `content-digest` and `idempotency-key` on POST and PATCH (on GET and HEAD, `idempotency-key` exactly when it is sent), then `x-sa-environment`; with the parameters `created`, `nonce`, `keyid`, `alg` (`ed25519` or `ecdsa-p256-sha256`) and `tag=\"gap-v2\"`, in that order; a 64-byte signature; an `Idempotency-Key` of 1 to 255 visible ASCII characters (`!` to `~`, no spaces); `X-SA-Environment` of `TEST` or `PRODUCTION`; and no `Authorization` header beside them. Nothing about the credential was checked.","type":"https://secondappraisal.com/developers/errors/signature_profile_invalid"},"signature_expired":{"status":401,"title":"Signature outside its window","description":"The signature's `created` is more than 300 seconds before our clock, or more than 30 seconds after it. Sign each request when you send it; if every request is refused this way, check your clock.","type":"https://secondappraisal.com/developers/errors/signature_expired"},"target_uri_not_allowed":{"status":401,"title":"Host not accepted for signed requests","description":"The signed request was sent to a host that doesn't take signed requests. Send it to a public hostname (`secondappraisal.com`, `gap.secondappraisal.com` or `lenders.secondappraisal.com`), and sign that host in `@target-uri`.","type":"https://secondappraisal.com/developers/errors/target_uri_not_allowed"},"signature_invalid":{"status":401,"title":"Signature invalid","description":"The signature didn't verify. The answer is the same whatever failed: `keyid` names no signed credential of ours, or a revoked one; `alg` isn't the algorithm its key was registered with; the body doesn't match `Content-Digest`; or the signature isn't the credential's over the signature base built from the request as received. Rebuild the base from the exact request you sent, with `@target-uri` as a WHATWG URL parser serializes it (a `'` in a query is `%27`, an empty `?` is dropped), and check your signature with your public key.","type":"https://secondappraisal.com/developers/errors/signature_invalid"},"environment_mismatch":{"status":401,"title":"Wrong environment","description":"The signature verified, but `X-SA-Environment` isn't the credential's: `TEST` for a test credential, `PRODUCTION` for a live one.","type":"https://secondappraisal.com/developers/errors/environment_mismatch"},"credential_not_activated":{"status":401,"title":"Credential not activated","description":"The signature verified, but the signed credential hasn't been activated yet. Activate it first with a signed `POST /api/gap/v1/credentials/{id}/verify`, the one call an unactivated credential can make.","type":"https://secondappraisal.com/developers/errors/credential_not_activated"},"signature_replay":{"status":409,"title":"Signature replayed","description":"A request with this signature's `nonce` was already received for this credential with a signature that verified, so this one was refused before it was read further. A nonce is spent when its signature has verified, its body has matched `Content-Digest`, it came from an address the credential's IP allowlist holds (when it holds one) and the credential was within its rate limit, whatever that request is then answered; a request refused 403 `ip_not_allowed`, 503 `credentials_unavailable` because the allowlist couldn't be decided, 429 `rate_limited`, or 503 `platform_standby` because the nonce itself couldn't be written, hasn't spent it. Sign every request, a retry too, with a new nonce, and keep the same `Idempotency-Key` for a retry.","type":"https://secondappraisal.com/developers/errors/signature_replay"},"public_key_in_use":{"status":409,"title":"Public key in use","description":"The signature verified, but another signed credential has already activated this public key, so this one can't be activated with it. A public key is active for one credential at most, and once activated it is never activated again. Generate a new key pair, register its public key in the portal and activate that credential. A replacement refused this way can be revoked in the portal, and the credential it replaced then replaced again with the new key.","type":"https://secondappraisal.com/developers/errors/public_key_in_use"},"edge_auth_required":{"status":403,"title":"Edge authentication required","description":"The request reached our platform directly instead of through SecondAppraisal's edge, so it was refused before anything was read or written. Send it to the public hostname (`secondappraisal.com` or `gap.secondappraisal.com`), never to a platform address.","type":"https://secondappraisal.com/developers/errors/edge_auth_required"},"platform_standby":{"status":503,"title":"Platform on standby","description":"Answering the request needed a write the platform couldn't make just then: our standby, during a failover, makes no writes (nothing was written), and the primary pauses writes for a few seconds during a planned switchover. Send the same request again after the seconds in `Retry-After`, with the same `Idempotency-Key` where the operation takes one. A signed request is signed anew, with a new nonce: whether this one's nonce was spent depends on which write was refused.","type":"https://secondappraisal.com/developers/errors/platform_standby"},"membership_program_closed":{"status":404,"title":"Membership closed to institutions","description":"The Garage Hub Membership is not open to institutions yet; every plan operation answers 404 until it is.","type":"https://secondappraisal.com/developers/errors/membership_program_closed"},"membership_not_enabled":{"status":403,"title":"Membership not enabled","description":"The SecondAppraisal team has not turned the membership on for this institution yet.","type":"https://secondappraisal.com/developers/errors/membership_not_enabled"},"membership_rider_unsigned":{"status":403,"title":"Membership rider unsigned","description":"Sign the updated partner agreement, with its Garage Hub Membership rider, on the Agreement page before enrolling vehicles.","type":"https://secondappraisal.com/developers/errors/membership_rider_unsigned"},"membership_billing_mode_required":{"status":403,"title":"Membership billing mode required","description":"Choose partner-billed or direct-collect billing in Settings before enrolling vehicles.","type":"https://secondappraisal.com/developers/errors/membership_billing_mode_required"},"membership_billing_method_required":{"status":403,"title":"Billing method required","description":"Partner-billed membership fees are invoiced monthly, so add a card on file or invoice terms before enrolling vehicles.","type":"https://secondappraisal.com/developers/errors/membership_billing_method_required"},"msa_required":{"status":403,"title":"Agreement not executed","description":"Referrals can be submitted once the Master Service Agreement is executed (the Agreement step in onboarding).","type":"https://secondappraisal.com/developers/errors/msa_required"},"billing_required":{"status":403,"title":"Billing method required","description":"Provider-paid and split-pay referrals need a billing method (the Billing step in onboarding). Customer-paid referrals don't.","type":"https://secondappraisal.com/developers/errors/billing_required"},"email_required_sms_disabled":{"status":422,"title":"Borrower email required","description":"Text invitations are off for the loss state, so an invitation referral needs `borrower_email`. A warm-handoff referral may be phone-only.","type":"https://secondappraisal.com/developers/errors/email_required_sms_disabled"},"script_version_stale":{"status":409,"title":"Disclosure script out of date","description":"The `disclosure.script_version` names a script that is not the current one. Deliver the current script to the borrower, then attest.","type":"https://secondappraisal.com/developers/errors/script_version_stale"},"duplicate_reference":{"status":409,"title":"Duplicate referral","description":"A referral with this reference already exists.","type":"https://secondappraisal.com/developers/errors/duplicate_reference"},"invalid_cursor":{"status":400,"title":"Invalid cursor","description":"`starting_after` must be the id of one of your own referrals.","type":"https://secondappraisal.com/developers/errors/invalid_cursor"},"not_found":{"status":404,"title":"Not found","description":"No record with this id belongs to your institution (in this key's mode).","type":"https://secondappraisal.com/developers/errors/not_found"},"referral_locked":{"status":409,"title":"Referral locked","description":"Borrower, vehicle, claim and program fields lock once the borrower activates. `loan_payoff_cents` and `deductible_cents` stay editable.","type":"https://secondappraisal.com/developers/errors/referral_locked"},"cancel_not_allowed":{"status":409,"title":"Cancel not allowed","description":"A referral can be cancelled only before the borrower activates.","type":"https://secondappraisal.com/developers/errors/cancel_not_allowed"},"economics_locked":{"status":409,"title":"Program terms locked","description":"The program terms locked when the borrower activated under them.","type":"https://secondappraisal.com/developers/errors/economics_locked"},"nothing_to_update":{"status":400,"title":"Nothing to update","description":"The body names no field this operation can change.","type":"https://secondappraisal.com/developers/errors/nothing_to_update"},"idempotency_key_mode_conflict":{"status":409,"title":"Idempotency-Key used in the other mode","description":"This Idempotency-Key belongs to the other mode (live or test). Keys are per mode: the same key sent with a live key and a test key makes two records. But a key already held by a record of the other mode can't replay it, and a live key can't send a key that starts with `test:`, which names a test key's request. Nothing is written; send a new key.","type":"https://secondappraisal.com/developers/errors/idempotency_key_mode_conflict"},"state_not_served":{"status":422,"title":"State not served","description":"We can't act as the appraiser for a vehicle in this state. The state judged is `garaged_state` when it is sent, else `loss_state`: the problem names it in `state`, and `basis` says which (`garaged` or `loss`). A referral with neither state is accepted.","type":"https://secondappraisal.com/developers/errors/state_not_served"},"duplicate_referral":{"status":409,"title":"Loss already referred","description":"Your institution already referred this loss (the same VIN and date of loss, or the same carrier and claim number) in this key's mode, and that referral is not cancelled. `existing_referral_id` names it.","type":"https://secondappraisal.com/developers/errors/duplicate_referral"},"simulate_requires_test_key":{"status":403,"title":"Test key required","description":"A simulation moves sandbox referrals only, so it needs a test key (`sa_gap_test_...` or `sa_test_...`).","type":"https://secondappraisal.com/developers/errors/simulate_requires_test_key"},"invalid_transition":{"status":409,"title":"Invalid transition","description":"The referral's status doesn't allow this step: a simulation moves a sandbox referral only along its lifecycle, and an attestation is accepted only while the referral waits for one.","type":"https://secondappraisal.com/developers/errors/invalid_transition"},"reporting_withdrawn":{"status":409,"title":"Reporting withdrawn","description":"The borrower withdrew permission to report this referral's progress to you, so it can't be changed: an edit, a cancel, an attestation and a simulation are each refused, before anything else about the request is checked against the referral. Nothing is written. Reading the referral shows `status: reporting_withdrawn` and the fields you supplied.","type":"https://secondappraisal.com/developers/errors/reporting_withdrawn"},"vin_already_live":{"status":409,"title":"VIN already enrolled","description":"The vehicle already has a live membership.","type":"https://secondappraisal.com/developers/errors/vin_already_live"},"vin_already_consulted":{"status":422,"title":"VIN already consulted","description":"The vehicle was the subject of a total-loss consultation with SecondAppraisal, so it can't be enrolled.","type":"https://secondappraisal.com/developers/errors/vin_already_consulted"},"state_required":{"status":422,"title":"Garaged state required","description":"Send `garaged_state` so we can confirm we can serve the vehicle there.","type":"https://secondappraisal.com/developers/errors/state_required"},"state_blocked":{"status":422,"title":"State not served","description":"We can't act as the appraiser in the vehicle's garaged state yet.","type":"https://secondappraisal.com/developers/errors/state_blocked"},"vin_invalid":{"status":422,"title":"Invalid VIN","description":"The VIN failed the checks SecondAppraisal applies before enrolling a vehicle (17 characters, no I, O or Q, a valid check digit).","type":"https://secondappraisal.com/developers/errors/vin_invalid"},"invalid_customer_price":{"status":422,"title":"Invalid member price","description":"A direct-collect member price must not be above the retail price.","type":"https://secondappraisal.com/developers/errors/invalid_customer_price"},"idempotency_key_reused":{"status":422,"title":"Idempotency-Key reused","description":"This key was already used for a different request: for an enrollment, another VIN or the other mode; for a referral, a body that differs from the first. Send a new key with a new request.","type":"https://secondappraisal.com/developers/errors/idempotency_key_reused"},"plan_not_enabled":{"status":403,"title":"Membership not enabled","description":"The Garage Hub Membership is not enabled for this institution's billing setup.","type":"https://secondappraisal.com/developers/errors/plan_not_enabled"},"not_live":{"status":409,"title":"Enrollment not live","description":"The enrollment has already ended.","type":"https://secondappraisal.com/developers/errors/not_live"},"not_a_member_vehicle":{"status":404,"title":"Not a member vehicle","description":"No live membership for this VIN under your institution.","type":"https://secondappraisal.com/developers/errors/not_a_member_vehicle"},"request_refused":{"status":400,"title":"Request refused","description":"A refusal with no more specific code. The response's `status` is the HTTP status it was sent with, and `error` gives the reason; send us the `request_id` if it is unclear.","type":"https://secondappraisal.com/developers/errors/request_refused"}},"x-data-classes":{"public":"A value with no meaning outside the API: a status name, a label, a count, an object type, or a timestamp of a public event.","institution_confidential":"The institution's own business records with SecondAppraisal: its references, record ids, program terms, prices and fees, statements and aggregate reports.","borrower_personal":"Facts about one borrower or member: their name, vehicle and VIN, insurance carrier and claim number, loss state and date, and the amounts of their loss, loan and settlement.","borrower_contact":"How to reach one borrower or member: an email address or a phone number.","credential":"A value that grants access by itself, such as a member's activation link."},"x-webhooks":{"referral.invited":{"post":{"operationId":"webhook.referral.invited","summary":"The borrower was sent the activation invitation.","description":"Sent with `X-SecondAppraisal-Event: referral.invited` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["referral.invited"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `referral.invited` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.invited"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReferralInvitedData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"referral.activated":{"post":{"operationId":"webhook.referral.activated","summary":"The borrower activated: the consultation exists.","description":"Sent with `X-SecondAppraisal-Event: referral.activated` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["referral.activated"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `referral.activated` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.activated"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReferralActivatedData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"referral.declined":{"post":{"operationId":"webhook.referral.declined","summary":"The borrower declined.","description":"Sent with `X-SecondAppraisal-Event: referral.declined` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["referral.declined"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `referral.declined` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.declined"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReferralData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"consultation.status_changed":{"post":{"operationId":"webhook.consultation.status_changed","summary":"The referral's provider-facing milestone moved.","description":"Sent with `X-SecondAppraisal-Event: consultation.status_changed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["consultation.status_changed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `consultation.status_changed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["consultation.status_changed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The referral and the milestone it reached. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"appraisal.completed":{"post":{"operationId":"webhook.appraisal.completed","summary":"Our appraisal report is complete.","description":"Sent with `X-SecondAppraisal-Event: appraisal.completed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["appraisal.completed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `appraisal.completed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["appraisal.completed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The referral and the milestone it reached. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"settlement.updated":{"post":{"operationId":"webhook.settlement.updated","summary":"The final settlement was recorded.","description":"Sent with `X-SecondAppraisal-Event: settlement.updated` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["settlement.updated"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `settlement.updated` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["settlement.updated"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookSettlementData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"charge.created":{"post":{"operationId":"webhook.charge.created","summary":"A charge for the referral was created.","description":"Sent with `X-SecondAppraisal-Event: charge.created` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["charge.created"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `charge.created` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["charge.created"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The referral and the charge (`id`, `amount_cents`, `status`). A charge on a referral whose borrower withdrew permission to report its progress still comes, with the referral as every read shows it: `status: reporting_withdrawn` and no progress. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_contact"}},"plan.enrollment.activated":{"post":{"operationId":"webhook.plan.enrollment.activated","summary":"A membership enrollment became active.","description":"Sent with `X-SecondAppraisal-Event: plan.enrollment.activated` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.enrollment.activated"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.enrollment.activated` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.enrollment.activated"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The enrollment's id, VIN, external_ref, status and dates; never the member's contact details. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_personal"}},"plan.enrollment.past_due":{"post":{"operationId":"webhook.plan.enrollment.past_due","summary":"A direct-collect membership's renewal is failing.","description":"Sent with `X-SecondAppraisal-Event: plan.enrollment.past_due` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.enrollment.past_due"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.enrollment.past_due` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.enrollment.past_due"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The enrollment's id, VIN, external_ref, status and dates; never the member's contact details. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_personal"}},"plan.enrollment.cancelled":{"post":{"operationId":"webhook.plan.enrollment.cancelled","summary":"A membership enrollment was cancelled or lapsed.","description":"Sent with `X-SecondAppraisal-Event: plan.enrollment.cancelled` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.enrollment.cancelled"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.enrollment.cancelled` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.enrollment.cancelled"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The enrollment's id, VIN, external_ref, status and dates; never the member's contact details. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_personal"}},"plan.benefit.redeemed":{"post":{"operationId":"webhook.plan.benefit.redeemed","summary":"A member's consultation was drawn against their membership.","description":"Sent with `X-SecondAppraisal-Event: plan.benefit.redeemed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.benefit.redeemed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.benefit.redeemed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.benefit.redeemed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The enrollment, the redemption id and the consultation number. Not sent once the borrower withdrew permission to report the progress of the referral the redemption names. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_personal"}},"plan.benefit.completed":{"post":{"operationId":"webhook.plan.benefit.completed","summary":"A member consultation finished.","description":"Sent with `X-SecondAppraisal-Event: plan.benefit.completed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.benefit.completed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.benefit.completed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.benefit.completed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The enrollment, the redemption id and the outcome. Not sent once the borrower withdrew permission to report the progress of the referral the redemption names. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"borrower_personal"}},"plan.statement.issued":{"post":{"operationId":"webhook.plan.statement.issued","summary":"A monthly membership statement was issued.","description":"Sent with `X-SecondAppraisal-Event: plan.statement.issued` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["plan.statement.issued"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `plan.statement.issued` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["plan.statement.issued"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"description":"The statement, as the statements operation returns it. Provisional: the key set is not frozen yet, so read it defensively.","x-provisional":true,"type":"object","properties":{},"additionalProperties":{}}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"institution_confidential"}},"review.completed":{"post":{"operationId":"webhook.review.completed","summary":"The referral's review completed: its review record has a new version.","description":"Sent with `X-SecondAppraisal-Event: review.completed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["review.completed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `review.completed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["review.completed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReviewRecordData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"institution_confidential"}},"review.updated":{"post":{"operationId":"webhook.review.updated","summary":"A review record already complete or closed has a new version.","description":"Sent with `X-SecondAppraisal-Event: review.updated` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["review.updated"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `review.updated` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["review.updated"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReviewRecordData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"institution_confidential"}},"referral.closed":{"post":{"operationId":"webhook.referral.closed","summary":"The referral closed: its review record's new version gives the close reason.","description":"Sent with `X-SecondAppraisal-Event: referral.closed` and `User-Agent: SecondAppraisal-Webhooks/1.0`. Which body an endpoint is sent is fixed when the endpoint is created, and its `payloadVersion` (`FULL_V1` or `THIN_V2`, in the response that created it and in the endpoint list) says which. A full-payload endpoint, one created before v2 keys and signed credentials opened (the partner security platform's `issue` stage), or created while that stage couldn't be read, is sent the first form: the full envelope, whose `data` is this type's. A thin-payload endpoint, created from `issue` on, is sent the second: `ThinEvent`, the thin event the events feed lists. The headers are the same for both.","parameters":[{"name":"X-SecondAppraisal-Signature","in":"header","required":true,"description":"`t=<unix seconds>,v1=<hex>`: v1 is HMAC-SHA256 with your endpoint's signing secret over `<t>.<raw body>`. The key is the secret's text for a `whsec_gap_` secret, and its base64-decoded bytes after `whsec_` for a Standard Webhooks secret. Verify it over the exact bytes received, compare in constant time, and refuse an old `t`. During a secret rotation's overlap (1 to 168 hours) it is signed with the previous secret, until you promote the new one or the overlap ends; switch your verifier to the new secret then.","schema":{"type":"string","pattern":"^t=\\d+,v1=[0-9a-f]{64}$"}},{"name":"X-SecondAppraisal-Event","in":"header","required":true,"schema":{"type":"string","enum":["referral.closed"]}},{"name":"X-SecondAppraisal-Delivery","in":"header","required":true,"description":"This delivery's id. A retry of the same delivery repeats it.","schema":{"type":"string"}},{"name":"webhook-id","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one (`whsec_` and base64). The event's id (`evt_…`); a retry repeats it.","schema":{"type":"string"}},{"name":"webhook-timestamp","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. Unix seconds, the same `t` as the signature header's.","schema":{"type":"string","pattern":"^\\d+$"}},{"name":"webhook-signature","in":"header","required":false,"description":"Standard Webhooks: sent only when your endpoint's signing secret is a Standard Webhooks one. `v1,<base64>` for each Standard Webhooks secret in force (during a rotation's overlap, the new one too), separated by spaces: HMAC-SHA256 keyed with the secret's base64-decoded bytes over `<webhook-id>.<webhook-timestamp>.<raw body>`. Any Standard Webhooks library verifies it; accept the delivery if any `v1` matches.","schema":{"type":"string","pattern":"^v1,[A-Za-z0-9+/]{43}=( v1,[A-Za-z0-9+/]{43}=)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"A `referral.closed` delivery: the full envelope to a full-payload endpoint, `ThinEvent` to a thin-payload one. This object and each object in it carry at least these fields; new fields may be added, so ignore any you don't recognise.","anyOf":[{"type":"object","properties":{"id":{"description":"The event id (`evt_…`). A retried delivery repeats it: use it to drop duplicates.","x-data-class":"public","type":"string"},"object":{"x-data-class":"public","type":"string","enum":["event"]},"type":{"x-data-class":"public","type":"string","enum":["referral.closed"]},"created_at":{"x-data-class":"public","type":"string","format":"date-time"},"livemode":{"description":"`true` for a live event, `false` for a sandbox one (from a test key's referral). Live events go to your live endpoints only, and sandbox referral events to your test endpoints only. The membership plan events don't carry it yet.","x-data-class":"public","type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookReviewRecordData"}},"required":["id","object","type","created_at","data"]},{"$ref":"#/components/schemas/ThinEvent"}]}}}},"responses":{"2XX":{"description":"Any 2xx acknowledges the delivery. Anything else, or no answer in 10 seconds, is retried with backoff."}},"x-data-class":"institution_confidential"}}}}