Client API

The client API lives under /api/v1/. All requests must be authenticated with your API key via the X-API-Key header.

X-API-Key: YOUR_API_KEY

Changelog

2026-09-28

  • We can now text the check link to your customer for you: pass sendSms: true when creating a check. SMS sending must be enabled for your account first.
  • New endpoint to resend the link by SMS.
  • Create and get responses now include an sms object (null unless we texted the link).
  • result now includes the back-of-device assessment: backConditionStatus, backSummary, backCaseOn, backCoverage and backAccessory.
  • New backConditionStatus value caution: a soft pass (see Back of device). If your integration rejects unknown status values, add caution before relying on back results.
  • New result.screenProtector field, recording what the customer said about a screen protector when screen damage was found.
  • Webhooks are now documented, including these new fields.

Endpoints

Create a check

POST /api/v1/checks

Creates a new device verification check and returns a link for your customer. Either send the link yourself, or set sendSms: true and we'll text it to them (see Sending the link by SMS). The link expires after 24 hours. Once opened, it can only be reopened within the next 10 minutes (for example, if the customer accidentally closes the page).

Request body

Field Type Required Description
customerRef string No Your internal reference (policy number, order ID, etc.)
make string No Expected device make (e.g. Apple)
model string No Expected device model (e.g. iPhone 14 Pro)
phoneNumber string No* Number of the phone being checked. Stored encrypted and never returned via the API. *Required with sendSms, in international format (e.g. +447700900000)
sendSms boolean No Text the link to phoneNumber for you. Requires SMS sending to be enabled for your account

make, model, and phoneNumber are encrypted at rest and used to cross-reference against data captured during the check.

Example request

curl -X POST https://getlustre.co.uk/api/v1/checks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerRef": "POL-12345",
    "make": "Apple",
    "model": "iPhone 14 Pro",
    "phoneNumber": "+447700900000",
    "sendSms": true
  }'

Response

{
  "checkId": "a1b2c3d4-...",
  "verificationUrl": "https://getlustre.co.uk/detect/start?token=abc123...",
  "expiresAt": "2024-01-16T10:30:00.000Z",
  "sms": {
    "status": "sent",
    "sentAt": "2024-01-15T10:30:01.000Z",
    "sendCount": 1,
    "error": null
  }
}

Without sendSms, sms is null: send verificationUrl to your customer yourself, by SMS or email. Either way, they must open it on the device being insured.

With sendSms, check sms.status in the response. If the text failed, the check still exists. See If a text fails.

Errors specific to this endpoint

Status When
400 sendSms is set but phoneNumber is missing or not in international format
403 sendSms is set but SMS sending isn't enabled for your account

In both cases no check is created, so it's safe to fix the request and retry.


POST /api/v1/checks/:checkId/sms

Texts the link again, for example if the customer lost the first message or the number was wrong. Works for checks created without sendSms too, as long as there's a phone number on the check or in the request.

It sends the same link. Resending doesn't create a new link or extend expiresAt.

Request body (optional)

Field Type Required Description
phoneNumber string No Send to this number instead, in international format. Defaults to the number on the check. The new number replaces the stored one, even if the text then fails

Example request

curl -X POST https://getlustre.co.uk/api/v1/checks/a1b2c3d4-.../sms \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phoneNumber": "+447700900001" }'

Response

{
  "checkId": "a1b2c3d4-...",
  "sms": {
    "status": "sent",
    "sentAt": "2024-01-15T11:02:00.000Z",
    "sendCount": 2,
    "error": null
  }
}

Errors specific to this endpoint

Status When
400 phoneNumber isn't in international format, or there's no number on the check and none was passed
403 SMS sending isn't enabled for your account, or the check belongs to another client
404 Check not found
409 The check is no longer open: it's complete, failed or expired. Create a new check instead
429 The link has already been texted 3 times for this check. See Limits

A failed text isn't an error response: you get 200 with sms.status: "failed". See If a text fails.


Get a check

GET /api/v1/checks/:checkId

Returns the full result of a check. Poll this after the customer completes the flow, or use it to retrieve results at any time.

Example request

curl https://getlustre.co.uk/api/v1/checks/a1b2c3d4-... \
  -H "X-API-Key: YOUR_API_KEY"

Response

{
  "checkId": "a1b2c3d4-...",
  "customerRef": "POL-12345",
  "status": "complete",
  "qrVerified": true,
  "createdAt": "2024-01-15T10:30:00.000Z",
  "openedAt": "2024-01-15T10:35:00.000Z",
  "completedAt": "2024-01-15T10:40:00.000Z",
  "expiresAt": "2024-01-16T10:30:00.000Z",
  "sms": {
    "status": "sent",
    "sentAt": "2024-01-15T10:30:01.000Z",
    "sendCount": 1,
    "error": null
  },
  "device": {
    "imei": "353456789012345",
    "make": "Apple",
    "model": "iPhone 14 Pro",
    "lookupBrand": "Apple",
    "lookupDevice": "Apple iPhone 14 Pro",
    "lookupModel": "A2650"
  },
  "result": {
    "conditionStatus": "pass",
    "summary": "No visible screen damage detected.",
    "screenProtector": null,
    "backConditionStatus": "caution",
    "backSummary": "Grip attached, visible panel undamaged",
    "backCaseOn": true,
    "backCoverage": "partial",
    "backAccessory": "grip"
  }
}

result is null until the check is complete.

Status values

Status Description
pending Link created, not yet opened by customer
in_progress Customer has opened the link
complete Customer completed the check
failed Check failed or was rejected
expired Link expired before the customer completed it

Screen

result.conditionStatus values

Value Description
pass Screen appears undamaged
damaged Screen damage detected
unclear The photo couldn't be assessed (out of frame, too blurry, wrong subject)
error The analysis failed to run

result.screenProtector values

A cracked screen protector looks just like a cracked screen. When damage is found, the customer is asked whether they have a protector on, and can only clear the damage by removing it and retaking the screen photo.

Value Meaning conditionStatus
null No damage found on the first photo, so the customer wasn't asked as analysed
none Customer confirmed there is no protector damaged
removed Customer removed the protector and retook the photo. The result reflects the bare screen. pass or damaged
not_removed Customer says the protector is cracked but couldn't remove it damaged

not_removed is recorded as damaged because the screen underneath couldn't be checked. It's a candidate for manual review, since the damage may only be in the protector.

Back of device

result.backConditionStatus values

Value Description
pass Back is fully visible and undamaged
caution Soft pass. The customer declared an accessory they can't remove, it covers part of the back, and the visible part is undamaged
damaged Damage detected on the visible part of the back, whether or not an accessory is attached
unclear The back couldn't be assessed: poor photo, or a declared accessory covers the whole back — review recommended
error The analysis failed to run
null No back result recorded

result.backCoverage values: how much of the back the AI found covered

Value Meaning
none Bare back panel fully visible
partial Something covers part of the back (e.g. a grip, ring or sticker) and the rest is visible
full A case, wallet or full skin hides most or all of the back
null Not recorded (checks completed before 2026-09-28)

backCaseOn is true whenever backCoverage is not none. It's kept for compatibility; prefer backCoverage.

result.backAccessory values: what the customer declared they can't remove

Value Meaning
grip Phone grip, ring or stand
skin Stuck-on skin or sticker
case Case that won't come off
other Something else
null Nothing declared

Customers are asked to remove any case or accessory first and can only continue with one attached by declaring it. An undeclared case can't be submitted.

An accessory can hide damage underneath it. Treat caution and a declared backAccessory as a factor in your decision rather than an equivalent of pass.

Reading the results together

Outcome Screen Back Suggested handling
Clear pass pass pass Accept
Pass after protector removed pass + screenProtector: "removed" any Treat the screen as a pass
Soft pass pass caution Accept with caution, e.g. note or exclude the covered area
Review unclear, or damaged + screenProtector: "not_removed" or unclear Manual review. Either side on its own is enough
Damaged damaged + screenProtector none or removed or damaged Decline or apply your damage process. Either side on its own is enough

sms: null unless we've texted the link for this check. See The sms object.

device fields

Field Source
imei Captured from device during check
make / model Provided by you when creating the check (decrypted)
lookupBrand / lookupDevice / lookupModel IMEI.info lookup result — independent verification of the device

phoneNumber is stored encrypted and is never returned via the API.


Instead of sending the verification link yourself, you can have us text it to your customer. Pass sendSms: true when you create a check, or call Resend the link by SMS later.

Before you start

SMS sending is off by default and has to be enabled for your account. Contact us to switch it on. Until then, any request using SMS returns 403.

Which number to use

Use the number of the phone being checked. The check only works when the link is opened on that device, so a link sent to a different phone (a partner's, a work phone) can't be completed there.

Numbers must be in international (E.164) format: +, country code, then the number, with no spaces or leading zero.

✅ Valid ❌ Invalid
+447700900000 07700900000 (no country code)
+14155552671 +44 7700 900000 (spaces)

What the customer receives

The text comes from our sender ID and starts with your account name, so the customer knows who it's from:

Acme Insurance: please complete your device check. Open this link on the phone you're checking: https://getlustre.co.uk/detect/start?token=…

Your account name is the name you were set up with. Contact us if it should read differently in texts.

The sms object

Returned by Create a check, Resend the link by SMS and Get a check. It's null if we've never texted the link for the check.

Field Description
status Outcome of the most recent attempt. sent: accepted by our SMS provider for delivery. failed: couldn't be sent, see error
sentAt When the link was last texted successfully
sendCount How many times the link has been texted successfully. Failed attempts aren't counted
error Why the most recent attempt failed, e.g. an invalid number. null after a successful send

A failed resend doesn't erase an earlier success. For example, after one successful text and one failed resend you'd see status: "failed", sendCount: 1, and sentAt from the first text.

sent means our SMS provider accepted the message, not that it reached the phone. Carriers can still fail to deliver it, for example if the phone is off for a long time. If the customer hasn't opened the link (the check is still pending), resend it or contact them another way.

If a text fails

The check is still created, and verificationUrl is still valid. You can:

  • Fix the number and resend by passing a corrected phoneNumber to Resend the link by SMS, or
  • Send verificationUrl yourself, by your own SMS or email.

Limits

  • 3 successful texts per check, including the first. Failed attempts don't count. After that, resending returns 429. Send verificationUrl yourself, or create a new check.
  • Only open checks. The link can only be texted while the check is pending or in_progress and before expiresAt. Resending never extends the expiry.

SMS and webhooks

Webhooks fire when a check is completed and don't include SMS status. Use Get a check to see the sms object.


Webhooks

If a webhook URL is configured for your account, we POST to it as soon as a customer completes a check.

{
  "checkId": "a1b2c3d4-...",
  "customerRef": "POL-12345",
  "status": "complete",
  "imei": "353456789012345",
  "qrVerified": true,
  "conditionStatus": "pass",
  "summary": "No visible screen damage detected.",
  "screenProtector": null,
  "backConditionStatus": "caution",
  "backSummary": "Grip attached, visible panel undamaged",
  "backCaseOn": true,
  "backCoverage": "partial",
  "backAccessory": "grip"
}

Field values match the result fields of Get a check.

  • Delivery is attempted once, with no retries, so don't rely on the webhook alone.
  • Requests aren't signed. Treat the webhook as a notification only and call GET /api/v1/checks/:checkId for the authoritative result.
  • Respond with any 2xx status. The response body is ignored.

Errors

Status Meaning
400 Missing or invalid request body (including a missing or badly formatted phoneNumber with sendSms)
401 Missing or invalid API key
403 Check belongs to a different client, or SMS sending isn't enabled for your account
404 Check not found
409 Check is no longer open (resending SMS)
429 SMS resend limit reached for this check
500 Server error

Typical integration flow

  1. Customer purchases insurance and provides their IMEI, make, model, and phone number
  2. Your system calls POST /api/v1/checks with that data
  3. You send the returned verificationUrl to the customer via SMS or email, or pass sendSms: true in step 2 and we text it to them
  4. Customer opens the link on their device and completes the check (~2 minutes)
  5. You receive a webhook (if configured) and/or call GET /api/v1/checks/:checkId to retrieve the result
  6. Cross-reference device.make / device.model against device.lookupBrand / device.lookupDevice, and use result.conditionStatus and result.backConditionStatus (see Reading the results together) to make your underwriting decision