Specify docs

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-sdk

2. Capture the event

Call capture() once the conversion is confirmed, with the user ID your site identified.

payments.ts
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

OptionRequiredDescription
advertiserKeyyesYour advertiser key, which starts with sak_
eventyesThe event name, such as purchase
identityyesThe user ID your site passes to identify(), or a wallet address
dedupeKeynoYour own ID for this event, such as an order ID
urlnoThe 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 with 0x without being a wallet address
  • a url that isn't absolute, or a dedupeKey longer 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:

payments.ts
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);
  }
}

Next steps

On this page