Skip to content

Conversation

@erayaydin
Copy link
Contributor

@erayaydin erayaydin commented Oct 14, 2025

This PR upgrades the Server SDK to the Server API v4. It removes Server APIv3 specific behavior and aligns the SDK with the new events format shared by Server API and Webhooks.

❓ Why?

  • Server API v3 is deprecated on Oct 31, 2025 and will be sunset on Oct 31, 2026.
  • v4 standardizes responses (no data/products/result nesting), switches to Bearer auth, unifies Webhooks & Server API event formats, and deprecates /visitors in favor of /v4/events.

⚙️ What Changed?

  • Endpoints & paths
    • GET /events/search -> GET /v4/events
    • GET /visitors -> removed, migrate to GET /v4/events
    • Request builder now constructs /v4/* URLs.
  • Authentication
    • Replace Auth-Api-Key custom header and apiKey query param with Authorization: Bearer <secret_api_key>.
    • Remove AuthenticationMode option.
  • Names
    • request_id -> event_id
    • Use snake_case in request and response models (e.g., linked_id, pagination_key)
  • Responses
    • Remove legacy top-level nesting
    • Align types with unified events schema
  • Search and Examples
    • Implement GET /v4/events with filters.
    • Update examples and mocked responses
  • Update Event
    • PUT /events -> PATCH /v4/event/:event_id
    • Body fields now snake_case
  • Docs
    • README updated for v4

⚠️ Breaking Changes

  • Auth: only Bearer is supported. Query and custom header API key modes and AuthenticationMode option are removed.
  • Endpoints/Signatures:
    • getEvent(requestId) -> getEvent(eventId)
    • updateEvent(body, eventId) now use snake_case fields
  • Models: v3 style top-level nesting removed. Consumers must map to the v4 unified event format.

📦 Migration Guide (SDK Consumers)

  • Remove authenticationMode option when initializing FingerprintJsServerApiClient.
const client = new FingerprintJsServerApiClient({
  apiKey: '<SECRET_API_KEY>',
  region: Region.Global,
-  authenticationMode: AuthenticationMode.AuthHeader
})
  • When triggering getEvent() function, use eventId parameter name instead of requestId.
- client.getEvent(requestId: "<request-id>")
+ client.getEvent(eventId: "<request-id>")
  • Use snake_case fields when updating an event.
client.updateEvent({
- linkedId: "linkedId"
+ linked_id: "linkedId"
}, "<event-id>")
  • Use tags instead of tag for updating an event.
client.updateEvent({
- tag: {
+ tags: {
    key: 'value',
  }, 
}, "<event-id>")
  • When triggering updateEvent() function, use eventId parameter name instead of requestId.
client.updateEvent(
  {},
- requestId: "<request-id>"
+ eventId: "<event-id>"
)
  • Removed deprecated getVisitorHistory() function.
  • Removed getVisits() function. Use searchEvents() function.
- client.getVisits('<visitorId>')
+ client.searchEvents({ visitor_id: "<visitorId>" })
  • Removed getRelatedVisitors() function.
  • Removed VisitorHistoryFilter, ErrorPlainResponse, VisitorsResponse, RelatedVisitorsResponse, RelatedVisitorsFilter, Webhook, EventsUpdateRequest types.

- Switch base URLs to APIv4 (`https://{region}.api.fpjs.io/v4`) and add
  `apiVersion` handling in URL builder.
- Use `Authorization: Bearer <secret-api-key>` only. Remove custom
  header and query api authorization.
- Event API renamed and updated. Use `event_id` instead of `request_id`.
  Also use `PATCH` for event update instead of `PUT` method.
- Add new examples, mocked responses, and update `sync.sh` script.
- README reworked for v4
- Package renamed to `@fingerprint/fingerprint-server-sdk`. Updated
  description and subpackages (example).
- Regenerated types and OpenAPI schema.
- Updated unit and mock tests for v4 URLs.

BREAKING CHANGES:
- Only **Bearer auth** is supported; query and custom-header API-key
  modes are removed. `AuthenticationMode` option is deleted.
- Event endpoint and signatures changes:
  - Use `client.getEvent(eventId)` instead of `requestId`

Related-Task: INTER-1488
@erayaydin erayaydin self-assigned this Oct 14, 2025
@erayaydin erayaydin added documentation Improvements or additions to documentation enhancement New feature or request labels Oct 14, 2025
@changeset-bot
Copy link

changeset-bot bot commented Oct 14, 2025

🦋 Changeset detected

Latest commit: bbf565b

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Remove `Accept: application/json` testing header for `updateEvent`
function.

Related-Task: INTER-1488
Fix updateEventTests test expected method to `PATCH`.

Related-Task: INTER-1488
@github-actions
Copy link
Contributor

github-actions bot commented Oct 14, 2025

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements
90.72% (-5% 🔻)
176/194
🟢 Branches
82.26% (-7.49% 🔻)
51/62
🟢 Functions
97.14% (-0.63% 🔻)
34/35
🟢 Lines
90.63% (-5.06% 🔻)
174/192
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢 urlUtils.ts
90% (-5.24% 🔻)
73.33% (-14.17% 🔻)
100%
89.74% (-5.38% 🔻)
🟢 serverApiClient.ts
84.44% (-5.11% 🔻)
70.59% (-2.14% 🔻)
100% (+10% 🔼)
84.44% (-5.11% 🔻)
🟢
... / getRetryAfter.ts
100%
50% (-50% 🔻)
100% 100%
🔴 responseUtils.ts
0% (-100% 🔻)
100%
0% (-100% 🔻)
0% (-100% 🔻)

Test suite run success

56 tests passing in 9 suites.

Report generated by 🧪jest coverage report action from bbf565b

Show full coverage report
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟢 All files 90.72 82.25 97.14 90.62
🟢  src 88.53 82 95.65 88.46
🟢   index.ts 100 100 100 100
🔴   responseUtils.ts 0 100 0 0 1-8
🟢   sealedResults.ts 100 100 100 100
🟢   serverApiClient.ts 84.44 70.58 100 84.44 32,72,123,127,171,264,278
🟢   types.ts 100 100 100 100
🟢   urlUtils.ts 90 73.33 100 89.74 33,43,60,166
🟡   utils.ts 75 100 100 75 6
🟢   webhook.ts 100 100 100 100
🟢  src/errors 100 83.33 100 100
🟢   apiErrors.ts 100 100 100 100
🟢   getRetryAfter.ts 100 50 100 100 2-3
🟢   handleErrorResponse.ts 100 100 100 100
🟢   unsealError.ts 100 100 100 100

@erayaydin erayaydin marked this pull request as ready for review October 15, 2025 12:42
@necipallef
Copy link
Contributor

necipallef commented Oct 21, 2025

Sharing feedback (more to come later):

  • [nitpick]: This line needs a closing quote. This mistake was already there before your PR, let's address it in your PR
  • I think we can safely remove getVisitorHistory example. This used to be a separate endpoint, that's why it had example, it is not the case any more.
  • The changelog file is still not mentioning the change to installation method. The command to install the library has changed because the package name is changed. This should be in the changelog.
  • The release says v8.0.0, it should be v7.0.0
  • The release is actually saying @fingerprint/[email protected] instead of just v8.0.0. IMO we should fix this before we release. This was a mistake that's done in all previous releases, not something this PR breaks, but something I want to see fixed.

I will try to wrap up my review ASAP, and share more if there is more. Thank you! @erayaydin


const headers = this.getHeaders()

const response = await this.fetch(url, {
Copy link
Contributor

@necipallef necipallef Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick]: We use getRequestPath to calculate the url, but we don't reuse all the options we provide to getRequestPath when making this fetch all. One example is the method param, we have to provide method: 'delete' twice, once in getRequestPath and once in fetch. This is not a healthy design. We can consider doing something like below:

function callApi(reqParams: ReqParams, headers: Headers) {
    const url = getRequestPath({
      path: reqParams.path,
      region: reqParams.region,
      pathParams: reqParams.pathParams,
      method: reqParams.method,
    })
     
    return this.fetch(url, {method: options.method, headers: headers})
}

The point is to make sure getRequestPath and this.fetch use the same params.

This can be treated as tech debt for later, up to you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, why do we even need method in getRequestPath?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the codebase:

// method mention here so that it can be referenced in JSDoc
// eslint-disable-next-line @typescript-eslint/no-unused-vars

@TheUnderScorer could you please give more context about this issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the structure of usage, but it still uses method. Could you please review the new structure @necipallef

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Feel free to resolve the conversation if no more input needed from anybody

*
* @param body - Data to update the event with.
* @param requestId The unique event [identifier](https://dev.fingerprint.com/docs/js-agent#requestid).
* @param eventId The unique event [identifier](https://dev.fingerprint.com/docs/js-agent#eventid).
Copy link
Contributor

@necipallef necipallef Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the link anchor is not found

export type EventsGetResponse = paths['/events/{event_id}']['get']['responses']['200']['content']['application/json']

/**
* More info: https://dev.fingerprintjs.com/docs/webhooks#identification-webhook-object-format
Copy link
Contributor

@necipallef necipallef Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}
updateEvent: {
parameters: {
query?: never
header?: never
path: {
/** @description The unique event [identifier](https://dev.fingerprint.com/reference/get-function#requestid). */
request_id: string
/** @description The unique event [identifier](https://dev.fingerprint.com/reference/get-function#event_id). */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid link anchor

Copy link
Contributor

@necipallef necipallef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left comments, some are nitpicks, some are change needed requests

Removes `example/getVisitorHistory.mjs` file because it's not a
different endpoint anymore.

Related-Task: INTER-1488
Add `fingerprint-server-sdk-smoke-tests` to changeset ignore list.

Related-Task: INTER-1488
Explicitly mention about package name change in changeset file.

Related-Task: INTER-1488
Use correct `patch` method instead of `put` for updating an event.

Related-Task: INTER-1488
Replace strict `response.status === 200` checks with `response.ok` in
fetch handlers.

Related-Task: INTER-1488
Added a new `callApi()` function to handle request building and `fetch`
usage in one place. Replaced all direct `fetch` calls with `callApi`.
Introduced `defaultHeaders` options to the client, it's include
`Authorization` header and allow extra headers and override of
`Authorization` header. Made `region` optional in `GetRequestPathOptions`
and it's default to `Region.Global` in `getRequestPath` function.

Related-Task: INTER-1488
Remove `isEmptyValue` helper function and use direct `== null` checks to
skip `undefined` or `null` values.

Related-Task: INTER-1488
Use correct `EVENT_ID` placeholder for the example `.env.example` dotenv
file.

Related-Task: INTER-1488
Add `IsNever` and `NonNeverKeys` utility types to filter out `never`
type keys. Introduce and export `AllowedMethod` that excludes specific
`parameters` and any `never` type methods. Use this `AllowedMethod` type
for `GetRequestPathOptions` type and `getRequestPath` functions. Update
related signatures.

Related-Task: INTER-1488
Use `options.method.toUpperCase()` when calling `fetch` to ensure
standard HTTP method casing and avoid test fails. Change `AllowedMethod`
to make a string literal union.

Related-Task: INTER-1488
Extend `callApi` to accept an optional `body?: BodyInit` and forward it
to `fetch`. Fix `updateEvent` to send `body`.

Related-Task: INTER-1488
Removed unnecessary visitor detail tests.

Related-Task: INTER-1488
@erayaydin erayaydin requested a review from necipallef October 22, 2025 13:13
Fix missing quote for `linked_id` in `searchEvents.mjs` example file.

Related-Task: INTER-1488
@necipallef
Copy link
Contributor

Can we also update this line from any to unknown? I found a few more any usage in types, I leave them up to you @erayaydin but I at least insist on changing this line.

Removed redundant `await` keyword in `callApi` function.

Related-Task: INTER-1488
@ilfa ilfa changed the title Migrate SDK to APIv4 Migrate SDK to APIv4 - INTER-1488 Oct 22, 2025
@ilfa ilfa changed the title Migrate SDK to APIv4 - INTER-1488 INTER-1488: Migrate SDK to APIv4 Oct 22, 2025

#### Webhook types

When handling [Webhooks](https://dev.fingerprint.com/docs/webhooks) coming from Fingerprint, you can cast the payload as the built-in `VisitWebhook` type:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When handling [Webhooks](https://dev.fingerprint.com/docs/webhooks) coming from Fingerprint, you can cast the payload as the built-in `VisitWebhook` type:
When handling [Webhooks](https://dev.fingerprint.com/reference/posteventwebhook#/) coming from Fingerprint, you can cast the payload as the built-in `Event` type:

Not sure about the link, but it should be more relevant.

import { Event } from '@fingerprint/fingerprint-server-sdk'

const visit = visitWebhookBody as unknown as VisitWebhook
const visit = visitWebhookBody as unknown as Event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const visit = visitWebhookBody as unknown as Event
const visit = eventWebhookBody as unknown as Event

Centralize response parsing and error handling in `callApi` function.
Throw consistent `SdkError`, `RequestError`, or `TooManyRequestsError`
depends on the case. Added `SuccessJsonOrVoid<Path, Method>` to
automatically resolve the potential/correct return type for success
responses. Simplify all public methods.

Related-Task: INTER-1488
Removed `handleErrorResponse` and moved all error logic into `callApi`.
Exported `isErrorResponse` to detect valid error payloads. Added
`createResponse` helper for creating mock Response object with headers.

Related-Task: INTER-1488
@github-actions
Copy link
Contributor

🚀 Following releases will be created using changesets from this PR:

@fingerprint/[email protected]

Major Changes

  • Server APIv3 -> Server APIv4 migration

    • Switch all endpoints to /v4/*.
    • Remove authenticationMode option when initializing FingerprintJsServerApiClient.
    • Rename request_id to event_id.
    • Use snake_case fields when updating an event.
    • Use PATCH method when updating an event.
    • Examples, tests, and docs updated.

    BREAKING CHANGES

    • authenticationMode option removed.
    • Endpoints and method signatures changed.
      • Use eventId instead of requestId when triggering updateEvent() function.
      • Use eventId instead of requestId when triggering getEvent() function.
    • Removed getVisits() function.
    • Removed getRelatedVisitors() function.
    • Removed VisitorHistoryFilter, ErrorPlainResponse, VisitorsResponse, RelatedVisitorsResponse,
      RelatedVisitorsFilter, Webhook, EventsUpdateRequest types.
    • Use tags instead of tag for updating an event.
    • Response models changed. (ce2854d)
  • change package name to @fingerprint/fingerprint-server-sdk (385b01b)

return jsonResponse as RelatedVisitorsResponse
let errPayload
try {
// TODO: Use ErrorJson<Path, Method> instead of ErrorResponse type. It requires generic error classes without error.message and error.code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional to keep this Todo item? Also, the ErrorJson type is not used anywhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants