Server events
Capture conversions your backend confirms, such as a completed payment.
Use server events for conversions your backend confirms: a payment clearing, an order shipping, an application being approved.
Identify in the browser too
A server event only counts for a user your site has identified with the browser SDK. Pass the same user ID in both places. Wallet addresses are the exception: they count from the server on their own.
Runnable example
nodejs-typescript is this page as a working Node.js app.
1. Install the SDK
npm i @specify-sh/advertiser-sdk2. Capture the event
Call capture() once the conversion is confirmed, with the user ID your site identified.
import { capture } from '@specify-sh/advertiser-sdk/server';
export async function onPaymentConfirmed(order: Order) {
await capture({
advertiserKey: process.env.SPECIFY_ADVERTISER_KEY!,
event: 'purchase',
identity: order.userId,
dedupeKey: order.id
});
}await it in serverless functions, so the request finishes before the function stops. It settles within about 18 seconds even if Specify is unreachable.
Options
| Option | Required | Description |
|---|---|---|
advertiserKey | yes | Your advertiser key, which starts with sak_ |
event | yes | The event name, such as purchase |
identity | yes | The user ID your site passes to identify(), or a wallet address |
dedupeKey | no | Your own ID for this event, such as an order ID |
url | no | The page where the event happened, as an absolute URL |
Avoid double counting
Pass a dedupeKey whenever the same event could be sent twice, such as from a webhook that retries. Events with the same name and dedupeKey count once. Without one, every call counts.
Errors
capture() throws ValidationError before sending anything when an option is invalid:
- an advertiser key that isn't
sak_followed by 30 characters - an empty event name
- an identity that is blank, contains
@, is longer than 256 bytes, or starts with0xwithout being a wallet address - a
urlthat isn't absolute, or adedupeKeylonger than 256 characters
It never rejects because Specify is unreachable, so a failure on our side can't break your handler. Catch ValidationError if the identity comes from data you don't control:
import { capture, ValidationError } from '@specify-sh/advertiser-sdk/server';
try {
await capture({ advertiserKey, event: 'purchase', identity: order.userId });
} catch (error) {
if (error instanceof ValidationError) {
console.error(error.message);
}
}