diff --git a/readme.md b/readme.md
index d0bd47f1..4d9e0778 100644
--- a/readme.md
+++ b/readme.md
@@ -25,110 +25,309 @@
-# FingerprintJS Server API Node.js SDK
-Node.js wrapper for [FingerprintJS Server API](https://dev.fingerprint.com/docs/server-api)
+# Fingerprint Server API Node.js SDK
-## Usage
+[Fingerprint](https://fingerprint.com) is a device intelligence platform offering 99.5% accurate visitor identification.
-Install package using npm
-``` sh
-npm i @fingerprintjs/fingerprintjs-pro-server-api
-```
+The Fingerprint Server Node SDK is an easy way to interact with the Fingerprint [Server API](https://dev.fingerprint.com/reference/pro-server-api) from your Node application. You can retrieve visitor history or individual identification events.
-Or install package using yarn
-``` sh
-yarn add @fingerprintjs/fingerprintjs-pro-server-api
-```
+## Requirements
+
+TypeScript support:
+
+- TypeScript 4.5.5 or higher
+
+Supported runtimes:
+
+- Node.js 18 LTS or higher (we support all [Node LTS releases before end-of-life](https://nodejs.dev/en/about/releases/)).
+- Deno and Bun might work but are not actively tested.
+- "Edge" runtimes might work with some modifications but are not actively tested.
+ See "edge" runtimes compatibility
+
+ This SDK can be made compatible with JavaScript "edge" runtimes that do not support all Node APIs, for example, [Vercel Edge Runtime](https://edge-runtime.vercel.app/), or [Cloudflare Workers](https://developers.cloudflare.com/workers/).
+
+ To make it work, replace the SDK's built-in `fetch` function (which relies on Node APIs) with the runtime's native `fetch` function. Pass the function into the constructor with proper binding:
+
+ ```js
+ const client = new FingerprintJsServerApiClient({
+ region: Region.EU,
+ apiKey: apiKey,
+ fetch: fetch.bind(globalThis),
+ });
+ ```
+
+
+
+## How to install
+
+Install the package using your favorite package manager:
+
+* NPM:
+
+ ```sh
+ npm i @fingerprintjs/fingerprintjs-pro-server-api
+ ```
+* Yarn:
+ ```sh
+ yarn add @fingerprintjs/fingerprintjs-pro-server-api
+ ```
+* pnpm:
+ ```sh
+ pnpm i @fingerprintjs/fingerprintjs-pro-server-api
+ ```
+
+## Getting started
+
+Initialize the client instance and use it to make API requests. You need to specify your Fingerprint [Secret API key](https://dev.fingerprint.com/docs/quick-start-guide#server-api) and the region of your Fingerprint application.
-### Usage of the Server API
```ts
import { FingerprintJsServerApiClient, Region } from '@fingerprintjs/fingerprintjs-pro-server-api';
-// Init client with the given region and the secret api_key
-const client = new FingerprintJsServerApiClient({region: Region.Global, apiKey: ""});
+const client = new FingerprintJsServerApiClient({
+ apiKey: '',
+ region: Region.Global,
+});
-// Get visitor history
-client.getVisitorHistory("").then(visitorHistory => {
- console.log(visitorHistory);
+// Get visit history of a specific visitor
+client.getVisitorHistory('').then((visitorHistory) => {
+ console.log(visitorHistory);
});
+// Get a specific identification event
+client.getEvent('').then((event) => {
+ console.log(event);
+});
```
-### Usage of the built-in webhook visit type
+### Using with TypeScript
+
+#### 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:
+
```ts
const visit = visitWebhookBody as unknown as VisitWebhook;
```
-### Compatibility
-This SDK might be also compatible, however not actively tested, with JavaScript "edge" runtimes that do not support all Node APIs, for example, [Vercel Edge Runtime](https://edge-runtime.vercel.app/), or [Cloudflare Workers](https://developers.cloudflare.com/workers/).
+#### Narrowing error types
-To make it work, replace the SDK's built-in fetch function (which relies on Node APIs) with the runtime's native `fetch` function. Pass the function into the constructor with proper binding:
-```js
-const client = new FingerprintJsServerApiClient({
- region: Region.EU,
- apiKey: apiKey,
- fetch: fetch.bind(globalThis),
-});
+The `getEvent` and `getVisitorHistory` methods can throw `EventError` and `VisitorsError`.
+You can use the provided `isVisitorsError` and `isEventError` type guards to narrow down error types:
+
+```typescript
+import { isVisitorsError, isEventError } from '@fingerprintjs/fingerprintjs-pro-server-api';
+
+client
+ .getVisitorHistory('', filter)
+ .then((result) => console.log(result))
+ .catch((err) => {
+ if (isVisitorsError(err)) {
+ if (err.code === 429) {
+ // VisitorsError429 type
+ retryLater(err.retryAfter); // this function needs to be implemented on your side
+ } else {
+ console.log('error: ', err.error);
+ }
+ } else {
+ console.log('unknown error: ', err);
+ }
+ });
+
+client
+ .getEvent('')
+ .then((result) => console.log(result))
+ .catch((err) => {
+ if (isEventError(err)) {
+ console.log(`error ${err.code}: `, err.error.message);
+ } else {
+ console.log('unknown error: ', err);
+ }
+ });
```
-## API
+## API Reference
+
+### `constructor({region: Region, apiKey: string})`
-#### `FingerprintJsServerApiClient({region: Region, apiKey: string})` constructor
Creates an instance of the client.
-##### Usage
+
+#### Usage
+
```js
-const client = new FingerprintJsServerApiClient({region: Region.EU, apiKey: ""});
+const client = new FingerprintJsServerApiClient({ region: Region.EU, apiKey: '' });
```
-##### Params
+
+#### Params
+
- `region: Region` - a region of the server, possible values: `Region.EU`, `Region.AP`, or `Region.Global`
- `apiKey: string` - secret API key from the [FingerprintJS dashboard](https://dashboard.fingerprint.com/)
- `fetch?: typeof fetch` - optional implementation of `fetch` function (defaults to `node-fetch`)
+
+---
+
+### `getEvent(requestId: string): Promise`
+
+Retrieves a specific identification event with the information from each activated product — Identification and all active [Smart signals](https://dev.fingerprint.com/docs/smart-signals-overview).
+
+#### Usage
+
+```typescript
+client
+ .getEvent('')
+ .then((eventInfo) => {
+ console.log(eventInfo);
+ })
+ .catch((error) => {
+ if (error.status === 403 || error.status === 404) {
+ console.log(error.code, error.message);
+ }
+ });
+```
+
+#### Params
+
+- `requestId: string` - identifier of the event
+
+#### Returns
+
+- `Promise` - promise with event response
+
+##### `EventResponse`
+
+For more information, see the [Server API documentation](https://dev.fingerprint.com/reference/getevent).
+
+```json
+{
+ "products": {
+ "identification": {
+ "data": {
+ "visitorId": "Ibk1527CUFmcnjLwIs4A9",
+ "requestId": "0KSh65EnVoB85JBmloQK",
+ "incognito": true,
+ "linkedId": "somelinkedId",
+ "time": "2019-05-21T16:40:13Z",
+ "timestamp": 1582299576512,
+ "url": "https://www.example.com/login",
+ "ip": "61.127.217.15",
+ "ipLocation": {
+ "accuracyRadius": 10,
+ "latitude": 49.982,
+ "longitude": 36.2566,
+ "postalCode": "61202",
+ "timezone": "Europe/Dusseldorf",
+ "city": {
+ "name": "Dusseldorf"
+ },
+ "continent": {
+ "code": "EU",
+ "name": "Europe"
+ },
+ "country": {
+ "code": "DE",
+ "name": "Germany"
+ },
+ "subdivisions": [
+ {
+ "isoCode": "63",
+ "name": "North Rhine-Westphalia"
+ }
+ ]
+ },
+ "browserDetails": {
+ "browserName": "Chrome",
+ "browserMajorVersion": "74",
+ "browserFullVersion": "74.0.3729",
+ "os": "Windows",
+ "osVersion": "7",
+ "device": "Other",
+ "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ...."
+ },
+ "confidence": {
+ "score": 0.97
+ },
+ "visitorFound": true,
+ "firstSeenAt": {
+ "global": "2022-03-16T11:26:45.362Z",
+ "subscription": "2022-03-16T11:31:01.101Z"
+ },
+ "lastSeenAt": {
+ "global": "2022-03-16T11:28:34.023Z",
+ "subscription": null
+ }
+ }
+ },
+ "botd": {
+ "data": {
+ "bot": {
+ "result": "notDetected"
+ },
+ "url": "https://example.com/login",
+ "ip": "61.127.217.15",
+ "time": "2019-05-21T16:40:13Z"
+ }
+ }
+ }
+}
+```
+
---
-#### `client.getVisitorHistory(visitorId: string, filter?: VisitorHistoryFilter): Promise`
-Gets history for the given visitor and given filter, returns a promise with visitor history response.
-##### Usage
+### `getVisitorHistory(visitorId: string, filter?: VisitorHistoryFilter): Promise`
+
+Retrieves event history for the specific visitor using the given filter, returns a promise with visitor history response.
+
+#### Usage
+
```js
-client.getVisitorHistory("", filter)
- .then(visitorHistory => {
+client
+ .getVisitorHistory('', filter)
+ .then((visitorHistory) => {
console.log(visitorHistory);
})
- .catch(error => {
+ .catch((error) => {
if (error.status === 403) {
console.log(error.error);
} else if (error.status === 429) {
- retryLater(error.retryAfter); // this function needs to be implemented on your side
+ retryLater(error.retryAfter); // this function needs to be implemented on your side
}
});
```
-##### Params
+
+#### Params
+
- `visitorId: string` - identifier of the visitor
-- `filter?: VisitorHistoryFilter` - visitor history filter, more info in [the API documentation](https://dev.fingerprint.com/docs/server-api#query-parameters)
-##### Returns
-- `Promise` - promise with visitor history response
----
-#### `VisitorHistoryFilter`
-Filter for querying API - see [query parameters](https://dev.fingerprint.com/docs/server-api#query-parameters).
-##### Usage
+- `filter?: VisitorHistoryFilter` - visitor history filter (details below)
+
+##### `VisitorHistoryFilter`
+
+Filter for querying the [visitors Server API endpoint](https://dev.fingerprint.com/reference/getvisits).
+
+Usage:
+
```js
const filter = {
- request_id: "",
- linked_id: "",
- limit: 5,
- paginationKey: ""
+ request_id: '',
+ linked_id: '',
+ limit: 5,
+ paginationKey: '',
};
```
-##### Properties
+
+Properties:
+
- `request_id: string` - filter visits by `requestId`.
Every identification request has a unique identifier associated with it called `requestId`. This identifier is returned to the client in the identification [result](https://dev.fingerprint.com/docs/js-agent#requestid). When you filter visits by `requestId`, only one visit will be returned.
+
- `linked_id: string` - filter visits by your custom identifier.
You can use [`linkedId`](https://dev.fingerprint.com/docs/js-agent#linkedid) to associate identification requests with your own identifier, for example: session ID, purchase ID, or transaction ID. You can then use this `linked_id` parameter to retrieve all events associated with your custom identifier.
+
- `limit: number` - limit scanned results.
For performance reasons, the API first scans some number of events before filtering them. Use `limit` to specify how many events are scanned before they are filtered by `requestId` or `linkedId`. Results are always returned sorted by the timestamp (most recent first). By default, the most recent 100 visits are scanned, the maximum is 500.
-- `paginationKey: string` - use `paginationKey` to get the next page of results.
+
+- `paginationKey: string` - use `paginationKey` to get the next page of results.
When more results are available (e.g., you requested 200 results using `limit` parameter, but a total of 600 results are available), the `paginationKey` top-level attribute is added to the response. The key corresponds to the `requestId` of the last returned event. In the following request, use that value in the `paginationKey` parameter to get the next page of results:
@@ -136,9 +335,15 @@ const filter = {
2. Use `response.paginationKey` to get the next page of results: `GET api-base-url/visitors/:visitorId?limit=200&paginationKey=1683900801733.Ogvu1j`
Pagination happens during scanning and before filtering, so you can get less visits than the `limit` you specified with more available on the next page. When there are no more results available for scanning, the `paginationKey` attribute is not returned.
----
-#### Server `VisitorsResponse` response
-Find more info in [the API documentation](https://dev.fingerprint.com/docs/server-api#response)
+
+#### Returns
+
+- `Promise` - promise with the visitor history response
+
+##### `VisitorsResponse`
+
+For more information, see the [Server API documentation](https://dev.fingerprint.com/reference/getvisits).
+
```json
{
"visitorId": "Ibk1527CUFmcnjLwIs4A9",
@@ -183,7 +388,7 @@ Find more info in [the API documentation](https://dev.fingerprint.com/docs/serve
"os": "Windows",
"osVersion": "7",
"device": "Other",
- "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ....",
+ "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ...."
},
"confidence": {
"score": 0.97
@@ -204,133 +409,10 @@ Find more info in [the API documentation](https://dev.fingerprint.com/docs/serve
}
```
-#### `client.getEvent(requestId: string): Promise`
-Get events with all the information from each activated product - Bot Detection and Identification.
-##### Usage
-```typescript
-client.getEvent("")
- .then(eventInfo => {
- console.log(eventInfo);
- })
- .catch(error => {
- if (error.status === 403 || error.status === 404) {
- console.log(error.code, error.message);
- }
- });
-```
-##### Params
-- `requestId: string` - identifier of the event
-##### Returns
-- `Promise` - promise with event response
----
-#### Server `EventResponse` response
-Find more info in the [API documentation](https://dev.fingerprint.com/docs/server-api#response-1).
-```json
-{
- "products": {
- "identification": {
- "data": {
- "visitorId": "Ibk1527CUFmcnjLwIs4A9",
- "requestId": "0KSh65EnVoB85JBmloQK",
- "incognito": true,
- "linkedId": "somelinkedId",
- "time": "2019-05-21T16:40:13Z",
- "timestamp": 1582299576512,
- "url": "https://www.example.com/login",
- "ip": "61.127.217.15",
- "ipLocation": {
- "accuracyRadius": 10,
- "latitude": 49.982,
- "longitude": 36.2566,
- "postalCode": "61202",
- "timezone": "Europe/Dusseldorf",
- "city": {
- "name": "Dusseldorf"
- },
- "continent": {
- "code": "EU",
- "name": "Europe"
- },
- "country": {
- "code": "DE",
- "name": "Germany"
- },
- "subdivisions": [
- {
- "isoCode": "63",
- "name": "North Rhine-Westphalia"
- }
- ],
- },
- "browserDetails": {
- "browserName": "Chrome",
- "browserMajorVersion": "74",
- "browserFullVersion": "74.0.3729",
- "os": "Windows",
- "osVersion": "7",
- "device": "Other",
- "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ....",
- },
- "confidence": {
- "score": 0.97
- },
- "visitorFound": true,
- "firstSeenAt": {
- "global": "2022-03-16T11:26:45.362Z",
- "subscription": "2022-03-16T11:31:01.101Z"
- },
- "lastSeenAt": {
- "global": "2022-03-16T11:28:34.023Z",
- "subscription": null
- }
- }
- },
- "botd": {
- "data": {
- "bot": {
- "result": "notDetected"
- },
- "url": "https://example.com/login",
- "ip": "61.127.217.15",
- "time": "2019-05-21T16:40:13Z"
- }
- }
- }
-}
-```
-### Using with TypeScript
-
-`getEvent` and `getVisitorHistory` methods return generic types `Promise` and `Promise` and throw `EventError` and `VisitorsError`.
+## Support and feedback
-You can use typeguards to narrow error types as in example below.
+To report problems, ask questions or provide feedback, please use [Issues](https://github.com/fingerprintjs/fingerprintjs-pro-server-api-node-sdk/issues). If you need private support, you can email us at [oss-support@fingerprint.com](mailto:oss-support@fingerprint.com).
-```typescript
-import { isVisitorsError, isEventError } from '@fingerprintjs/fingerprintjs-pro-server-api';
+## License
-client
- .getVisitorHistory("", filter)
- .then(result => console.log(result))
- .catch(err => {
- if (isVisitorsError(err)) {
- if (err.code === 429) {
- // VisitorsError429 type
- retryLater(err.retryAfter); // this function needs to be implemented on your side
- } else {
- console.log('error: ', err.error)
- }
- } else {
- console.log('unknown error: ', err)
- }
- });
-
-client
- .getEvent("")
- .then(result => console.log(result))
- .catch(err => {
- if (isEventError(err)) {
- console.log(`error ${err.code}: `, err.error.message)
- } else {
- console.log('unknown error: ', err)
- }
- });
-```
+This project is licensed under the [MIT license](./LICENSE).