Quickstart

Five steps from a new key to a referral you can follow. Every call goes to https://secondappraisal.com/api/gap/v1.

1.Create a key

At launchOn the portal's API & Webhooks page, a seat that manages credentials creates v2 keys, after proving a fresh second factor. The form starts on a test key, which starts sa_test_ and lasts at most 90 days. A live key starts sa_live_, lasts at most 365 days, and needs an executed Master Service Agreement and your institution's security contact on file. Each key holds only the permissions you choose: a preset (Intake only, Intake + status, Reporting, Membership roster) or one by one. A live key that holds referrals.contact:read, as the Intake + status and Membership roster presets do, also needs an IP allowlist, set in the API console: once the partner security platform enforces, the console refuses one without it with 422 live_key_policy.

The key is shown once: keep it in a secrets manager, never in code, a ticket or an email. Build with a test key.

Until launch, the page creates live keys only, so there is no test key to build with yet. A legacy key you already hold keeps working: a legacy live key (sa_gap_) until we schedule its retirement, with notice, and a legacy test key (sa_gap_test_) until the expiry GET /api/gap/v1/me reports: 90 days after it was created, or, for a key made before keys had an expiry, 90 days after we stamped it; we may schedule its retirement sooner, with notice.

2.Make your first call

GET /api/gap/v1/me answers any key, live or test, with what it is, what it may do and when it expires. Make it first, and again whenever a key is refused elsewhere.

Request
curl https://secondappraisal.com/api/gap/v1/me \
  -H "Authorization: Bearer $SECONDAPPRAISAL_TEST_KEY"
200 response
{
  "id": "clqz8k2p70000ab2cd3ef4gh5",
  "object": "gap.api_key",
  "prefix": "sa_test_4Xh9",
  "mode": "test",
  "format": "v2",
  "auth_method": "secret",
  "permissions": [
    "referrals:write",
    "referrals:read",
    "referrals.contact:read"
  ],
  "expires_at": "2027-01-05T17:00:00.000Z",
  "institution": {
    "id": "clqz8k2p70002ab2cd3ef4gh7",
    "status": "active"
  },
  "allowed_cidr_count": 0
}

3.Create a referral

A referral made with a live key is real: we invite the borrower. Make your test referrals with a test key, which at launch creates sandbox referrals that reach no one (see the sandbox). Until the sandbox opens, a test key's referral call is answered 403 test_key_referrals_unavailable.

Send an Idempotency-Key with every create, such as your own reference for the loss. After a timeout or a 5xx, send the same request again with the same key: it answers 200 with the original referral and idempotent_replay: true if the first call created it, or creates it now (201) if it didn't; either way nothing is created twice.

At launchA retry that arrives while the first call is still being written can be answered 409 duplicate_referral: send it again a moment later and it answers the replay. With a v2 key, the same key sent with a different request is 422 idempotency_key_reused.

Request
curl -X POST https://secondappraisal.com/api/gap/v1/referrals \
  -H "Authorization: Bearer $SECONDAPPRAISAL_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: CASE-4471" \
  -d @referral.json
referral.json
{
  "borrower_first_name": "Jordan",
  "borrower_last_name": "Reyes",
  "borrower_email": "jordan.reyes@example.com",
  "borrower_phone": "(512) 555-0142",
  "vin": "1FTFW1ET5DFC10312",
  "vehicle_year": 2021,
  "vehicle_make": "Ford",
  "vehicle_model": "F-150",
  "primary_carrier": "Example Mutual",
  "claim_number": "EM-2026-004471",
  "loss_state": "TX",
  "date_of_loss": "2026-06-15",
  "initial_offer_cents": 2150000,
  "loan_payoff_cents": 2875000,
  "deductible_cents": 50000,
  "external_ref": "CASE-4471"
}

The first call answers 201 with the referral. A referral needs an executed Master Service Agreement, and provider-paid and split-pay referrals also need a billing method. A field a referral must never carry, such as an SSN, a date of birth or an account number, is refused with 400 prohibited_field before the rest of the body is validated, and its value is never stored.

4.Follow it

Read one referral, with its milestone timeline, or list yours, newest first:

Requests
curl https://secondappraisal.com/api/gap/v1/referrals/clqz8k2p70001ab2cd3ef4gh6 \
  -H "Authorization: Bearer $SECONDAPPRAISAL_TEST_KEY"

curl "https://secondappraisal.com/api/gap/v1/referrals?status=invited,activated&limit=25" \
  -H "Authorization: Bearer $SECONDAPPRAISAL_TEST_KEY"

Or let us tell you: register an HTTPS endpoint on the portal's API & Webhooks page, and we send a signed POST as each referral moves, from the invitation to the settlement. Verify every delivery before you act on it.

At launchGET /api/gap/v1/events lists your events from the last 30 days, for polling instead of, or beside, webhooks.

5.Handle refusals and limits

Every refusal the API makes is an RFC 9457 problem with a stable code: branch on it, never on the wording. Error codes lists each one and what to do. During a failover the platform can answer a write itself, before the API sees it, with a plain JSON 503 whose error is platform_standby and which has no code: nothing was written, so send it again after the seconds in Retry-After. During an outage our edge answers any request with a plain JSON 503 whose error is service_unavailable: nothing was processed, so send that again after the seconds in Retry-After too.

Each key may make 300 reads, 120 writes and 12 bulk calls a minute. Wait the number of seconds in Retry-After. After a 5xx, retry with the same Idempotency-Key, waiting the seconds in Retry-After when it is sent.

The API reference has every operation, field and code.