Skip to main content

Submit and retrieve device fingerprints

Integrate event submission on your server-side. Your app collects a fingerprint payload with the Stitch SDK. Following this, your server-side submits it to the Stitch API and optionally re-reads events or device signals.

API base: https://api.stitch.money/v2/

Token endpoint: https://secure.stitch.money/connect/token

Test vs live is determined by clientId, not a different base URL.

Client token

Event submission and reads are protected by a client token. Follow the steps described in the client token guide to obtain a client token with the client_fingerprinting scope.

POST https://secure.stitch.money/connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={client_id}
&client_secret={client_secret}
&scope=client_fingerprinting

Use the access token only on your servers. Never embed the client secret or access token in a mobile or web app.

Submit an event

POST https://api.stitch.money/v2/fingerprint/events
Authorization: Bearer {access_token}
Content-Type: application/json
{
"correlationId": "login-123",
"userId": "user-abc",
"fingerprint": "base64-sdk-payload",
"metadata": {
"action": "login",
"channel": "my-app-web"
}
}
FieldRequiredNotes
fingerprintYesExact SDK output string.
correlationIdNoOptional business reference for your logs and support (for example your login attempt or order reference). Max length 128 characters. Useful if a response is lost and you never received the event id. If omitted, Stitch assigns one and returns it. Not an idempotency key and not a nonce: the same value on a later POST still creates a new event, and GET ?correlationId= may return more than one.
userIdNoYour user identifier (merchant-supplied). Send when known (for example after login). Omit for anonymous or pre-auth events. Stored and returned; never used for device matching.
metadataNoFreeform customer context stored with the event. Use a flat JSON object. Suggested keys include action and channel. See Metadata.
Client id

Taken from the OAuth access token. clientId is not a request field. Do not send it in the body.

info

Use top-level correlationId for anything you may need to look up later, as this provides your support and lookup handle for a submit (GET /v2/fingerprint/events?correlationId=). Putting the same value only in metadata stores it, but Stitch will not query events by metadata keys.

Safe retries of the same HTTP request use the standard Stitch Gateway Idempotency-Key header (UUID v4). See Idempotency. That is separate from correlationId. Reusing a key with a different body (for example a new fingerprint payload) can return 409. Successful replays may include Idempotency-Replayed: true. Idempotency is best-effort if the backing store is unavailable.

Success response (HTTP 200)

FieldDescription
idStitch event resource id. Use for support and GET by event. Opaque; persist as returned.
correlationIdYour business reference if you sent one, otherwise one Stitch assigned. Not used for idempotency.
userIdEcho of the optional userId you sent, if any.
deviceIdOpaque device identifier scoped to your client. Persist as returned; do not parse structure from examples. Primary output for risk decisions. Presence means resolution succeeded. May change after prolonged inactivity; treat stored ids as soft references and handle 404 on device reads.
metadataYour metadata, echoed as submitted.
signalsCurated device intelligence. See Signals.
createdAtWhen Stitch received the event.

Success means HTTP 200 with a deviceId.

{
"id": "fpe_1Q0PsIJvEtkwdCNYMSaVuR",
"correlationId": "login-123",
"userId": "user-abc",
"deviceId": "fpd_2eyfk4nQpHs7iWQ4QwjJK5",
"metadata": {
"action": "login",
"channel": "my-app-web"
},
"signals": {
"platform": "web",
"ip": {
"address": "102.x.x.x",
"country": "ZA"
},
"device": {
"browser": { "name": "Chrome", "version": "126.0" },
"os": { "name": "macOS", "version": "15.1" },
"userAgent": "Mozilla/5.0 ...",
"screenResolution": { "width": 1440, "height": 900 },
"timezone": "Africa/Johannesburg",
"languages": ["en-ZA", "en"]
}
},
"createdAt": "2026-05-28T17:00:00Z"
}

Errors

Validation, auth, and processing failures use the standard Stitch API Gateway error response:

{
"code": "BAD_USER_INPUT",
"title": "Invalid Input",
"detail": "fingerprint is required"
}
HTTPTypical codeMeaning
400BAD_USER_INPUT / INVALID_IDEMPOTENCY_KEYInvalid request (for example missing fingerprint, oversized metadata, or a bad idempotency key).
401UNAUTHORIZEDMissing or invalid access token.
403FORBIDDENToken lacks the client_fingerprinting scope, or the client is not enabled.
409IDEMPOTENCY_KEY_ALREADY_USED / IDEMPOTENCY_REQUEST_IN_PROGRESSIdempotency conflict (same key, different body, or request still in flight).
500INTERNAL_SERVER_ERRORUnexpected failure. Retry the request if appropriate.

Do not invent a deviceId when the request fails.

Read APIs

All reads require a client token with the client_fingerprinting scope and are scoped to your client. Path parameters are resource primary keys only (id). Alternate lookups use query parameters.

Persist anything you need long-term from the POST response on your side. Do not rely on event GETs as an archive. GET /devices/{id} may return 200 with id / lastSeenAt and omit signals when a snapshot is unavailable.

Get event by id

GET https://api.stitch.money/v2/fingerprint/events/{id}
Authorization: Bearer {access_token}

Re-fetch a completed event (for example if a POST response was lost, or for support). This is not waiting on pending resolution. POST is synchronous and already terminal.

Use the id from the submit response. Do not put a correlationId or deviceId in this path; those are different lookups below. A wrong key returns 404.

On success, the response body matches the POST 200 success shape (id, deviceId, metadata, signals, etc.).

HTTPTypical codeMeaning
200-Event found; same success body as submit.
404NOT_FOUNDUnknown event id.
401UNAUTHORIZEDMissing or invalid access token.
403FORBIDDENToken lacks the client_fingerprinting scope, or the client is not enabled.

List events (paginated)

Event collections use the same pagination style as other Stitch REST list APIs: limit, offset, and hasNext. Results are newest first.

Pass one filter per request:

QueryPurpose
correlationIdFind events by the business reference you sent (for example a lost event id)
deviceIdEvent history for that device (newest first). Latest event only: add &limit=1.
GET https://api.stitch.money/v2/fingerprint/events?correlationId={correlationId}&limit=50&offset=0
GET https://api.stitch.money/v2/fingerprint/events?deviceId={deviceId}&limit=50&offset=0
Authorization: Bearer {access_token}
{
"data": [
{
"id": "fpe_1Q0PsIJvEtkwdCNYMSaVuR",
"correlationId": "login-123",
"deviceId": "fpd_2eyfk4nQpHs7iWQ4QwjJK5",
"signals": { }
}
],
"page": {
"limit": 50,
"offset": 0,
"hasNext": false
}
}
HTTPTypical codeMeaning
200-Page returned (data may be empty).
400BAD_USER_INPUTMissing filter, or invalid pagination params.
401UNAUTHORIZEDMissing or invalid access token.
403FORBIDDENToken lacks the client_fingerprinting scope, or the client is not enabled.

Get device by id

GET https://api.stitch.money/v2/fingerprint/devices/{id}
Authorization: Bearer {access_token}

Returns the device resource for a public device id you previously received (as deviceId on events). This is not an event.

FieldDescription
idPublic device id (same value as deviceId on events).
lastSeenAtLast successful resolve for this device+client.
signalsLatest curated signals snapshot when available; omit if aged out / unknown.

For the latest full event (including correlationId, metadata, etc.), use GET /v2/fingerprint/events?deviceId={id}&limit=1. For history, omit limit=1 and paginate. Submit a new POST when you need freshly collected intelligence.

{
"id": "fpd_2eyfk4nQpHs7iWQ4QwjJK5",
"lastSeenAt": "2026-07-29T12:00:00.000Z",
"signals": {
"platform": "web",
"ip": { "address": "102.x.x.x", "country": "ZA" },
"device": {
"browser": { "name": "Chrome", "version": "126.0" },
"os": { "name": "macOS", "version": "15.1" },
"screenResolution": { "width": 1440, "height": 900 }
}
}
}
HTTPTypical codeMeaning
200-Device known. May include signals, or only id / lastSeenAt if a signals snapshot is unavailable.
404NOT_FOUNDUnknown device id, or not owned by your client (not merely "signals expired").
401UNAUTHORIZEDMissing or invalid access token.
403FORBIDDENToken lacks the client_fingerprinting scope, or the client is not enabled.

Listing events by userId is not available in this release.

Metadata

Metadata is optional freeform context stored with the event and returned as submitted. It does not participate in identity resolution. Use a flat object.

A typical submit is fingerprint payload + optional userId + optional metadata.action (and any other keys you care about, such as channel).

Suggested keyExampleNotes
action"login"What the user was doing. Omit if you prefer not to share.
channel"my-app-web"Product surface. Example only.

Limits: Metadata must be a flat JSON object (no nested objects or arrays); nesting returns HTTP 400. Total size at or below 4KB UTF-8 bytes of the serialized JSON. Stitch does not validate individual value formats (no phone or email checks). Oversized metadata returns HTTP 400.

userId is not metadata. Send it as an optional top-level request field when known.

Signals

signals is curated device intelligence for this event, not a copy of the SDK payload. Keys are only included when known for that platform and event. A missing key means unknown, not false.

KeyPlatformsWhat it tells you
platformAllweb, ios, or android
ip.addressAllIP observed at SDK collection when signed fingerprintContext is present and valid. Absent if context is missing, expired, or not enabled for your client (missing = unknown, not an error). Signed context is short-lived (on the order of ~5 minutes). Collect close to submit; a queued or delayed POST can drop ip.* without failing the request.
ip.countryAllISO country code from that IP when attested context is available
device.osAllObject { "name", "version" } when available
device.browserWebObject { "name", "version" } when available
device.userAgentWhen availableUser agent string when available
device.screenResolutionWhen availableObject { "width", "height" } (numbers when known)
device.timezoneWhen availableDevice timezone
device.languagesWhen availableLanguage preferences
"signals": {
"platform": "web",
"ip": {
"address": "102.x.x.x",
"country": "ZA"
},
"device": {
"browser": { "name": "Chrome", "version": "126.0" },
"os": { "name": "macOS", "version": "15.1" },
"userAgent": "Mozilla/5.0 ...",
"screenResolution": { "width": 1440, "height": 900 },
"timezone": "Africa/Johannesburg",
"languages": ["en-ZA", "en"]
}
}

Additional browser and mobile integrity signals (bot, VPN, emulator, jailbreak, and similar) are phased and appear later as detectors ship. Stitch will communicate new signal availability as they become available; missing keys do not break deviceId or event id.

Not returned in signals: raw SDK attribute bags, matcher internals, or an IP “collection vs API submit” proof (API submit is your server-side, so that comparison is not meaningful).

Operations

TopicGuidance
NetworkAllow egress to *.stitch.money from the app (the SDK fetches config automatically).
Lost response / re-readGET /v2/fingerprint/events/{id}, or GET /v2/fingerprint/events?correlationId= if you never received the event id.
Device resource / latest signals snapshotGET /v2/fingerprint/devices/{id}; POST again for freshly collected signals.
Latest full event for a deviceGET /v2/fingerprint/events?deviceId={id}&limit=1 (newest first).
Event history for a deviceGET /v2/fingerprint/events?deviceId= with limit / offset pagination.
deviceId longevityExpect stability with regular use. After prolonged inactivity (months), a returning device may receive a new deviceId.
Local cacheThe SDK may return a recently cached fingerprint payload from a prior getDeviceData() call. That is still a local SDK result — you must still submit it from your server-side via POST /v2/fingerprint/events. Prefer calling getDeviceData() close to the action you care about when you want a fresh payload.
Safe retriesUse the Gateway Idempotency-Key header for the same HTTP request. Do not reuse a key with a different body. Idempotency is best-effort if the backing store is unavailable.