@straitlink/node
v0.8.0
Published
Server SDK for Node: create and manage short links and send conversion events from your backend. Zero dependencies.
Maintainers
Readme
@straitlink/node
Server SDK for Strait: create and manage links and send conversion events from your backend. Zero dependencies; uses your tenant API key.
Quick start (60 seconds)
1. Install.
npm install @straitlink/node2. Make a link from your server. Save this as quickstart.mjs. The secret key (st_live_…) is in Dashboard → Settings → Secret keys; keep it on servers only.
import { StraitClient } from '@straitlink/node';
const strait = new StraitClient({ apiKey: process.env.STRAIT_API_KEY, baseUrl: 'https://strait.link' });
const link = await strait.createLink({ slug: 'hello', longUrl: 'https://shop.example.com/product/42' });
console.log(link.shortUrl);STRAIT_API_KEY=st_live_… node quickstart.mjsYou should see: https://<your-handle>.strait.link/hello, and the same link in Dashboard → Links. Run it twice and the second run throws a StraitError with status 409, because the path is taken.
Platform features
| Feature | Status | Notes |
|---|---|---|
| Create, list, update and delete links | ◐ Beta | StraitClient, with your tenant API key. |
| Conversion events from your backend (sendEvent) | ◐ Beta | Pass the clickId from the app to tie an event to its tap. |
| iPhone install matching | ◐ Beta | Happens in your app (the mobile SDKs), not on the server. Your choice in Dashboard → Settings → iPhone installs: device matching, paste handoff, both or neither, read from Strait at runtime. Device matching is off by default. iPhone matches are labelled Estimated until measured. |
| Published on npm | ● Live | npm install @straitlink/node |
● Live · ◐ Beta · ○ Planned · – Not yet. The same words as the platform status page.
Install
Don't install other names that look similar (strait on npm is someone else's package).
npm install @straitlink/nodeUse
import { StraitClient } from '@straitlink/node';
const strait = new StraitClient({
apiKey: process.env.STRAIT_API_KEY!, // st_live_… from the dashboard
baseUrl: 'https://strait.link', // must be https (http only for localhost)
});
// Create a link
const link = await strait.createLink({
slug: 'launch',
longUrl: 'https://yourapp.com/launch',
tags: ['campaign'],
});
// List / update / pause
await strait.listLinks(); // every active link
await strait.listLinks({ limit: 100, status: 'paused' }); // one page: { links, nextCursor }
await strait.updateLink('launch', { longUrl: 'https://yourapp.com/launch-v2' });
await strait.deactivateLink('launch'); // paused, not deleted
// Send a conversion / revenue event (ties revenue to the funnel)
// Authenticated with your secret key — no publishable key needed server-side.
await strait.sendEvent({ event: 'purchase', value: 49.99, currency: 'USD' });
// Optional: the tap it came from (B15), e.g. forwarded by your app, so the
// dashboard places the revenue on that tap's channel / A/B variant.
await strait.sendEvent({ event: 'purchase', value: 49.99, currency: 'USD', clickId });Failures throw a StraitError with .status and the parsed .body (the raw text when the reply
is not JSON, such as a proxy's HTML 502 page).
API
Every secret-key operation of the API has a method named after its OpenAPI operationId
(openapi.json, pinned in this repo; test/openapi-coverage.test.ts
checks each one's HTTP method and path, and a daily job compares the pin with the live file).
| method | endpoint |
|--------|----------|
| createLink(input) | POST /v1/links |
| batchCreateLinks(links) | POST /v1/links/batch (up to 500) |
| getLink(slug) | GET /v1/links/:slug |
| listLinks() / listLinks({ limit, cursor, status }) | GET /v1/links |
| updateLink(slug, fields) | PATCH /v1/links/:slug |
| deactivateLink(slug) (deleteLink still works) | DELETE /v1/links/:slug (pauses it) |
| getLinkQr(slug, { format, size, … }) | GET /v1/links/:slug/qr → { contentType, body } |
| getAnalytics({ from, to, link, group }) | GET /v1/analytics |
| getUsage() | GET /v1/usage |
| getReferrals({ from, to }) | GET /v1/referrals |
| simulateTap({ url, ua, country }) | GET /v1/simulate |
| exportData({ part, from, to }) | GET /v1/export → { contentType, body } |
| createExportToken({ part, from, to }) | POST /v1/export/token |
| testWebhook(id) | POST /v1/webhooks/:id/test |
| listWebhookDeliveries(id, { limit, cursor }) | GET /v1/webhooks/:id/deliveries |
| redeliverWebhookDelivery(id) | POST /v1/webhook-deliveries/:id/redeliver |
| listDomains() / addDomain(hostname) / removeDomain(hostname) | /v1/domains |
| recordEvent(input) (sendEvent returns nothing) | POST /v1/event |
| verifyWebhook(rawBody, headers, secret, { toleranceSeconds }) | checks a webhook delivery (no request) |
Verify a webhook
Pass the raw body (before JSON parsing), the headers and your webhook's signing secret. It checks the
X-Strait-Signature HMAC in constant time and refuses deliveries more than 5 minutes old.
import express from 'express';
import { verifyWebhook } from '@straitlink/node';
const app = express();
app.post('/webhooks/strait', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyWebhook(req.body, req.headers, process.env.STRAIT_WEBHOOK_SECRET!)) return res.status(401).end();
const delivery = JSON.parse(req.body.toString('utf8')); // { id, event, createdAt, data }
res.status(204).end();
});Size and speed
Measured 25 Oct 2026 on 0.7.0.
| What | Size or time | How it was measured |
|---|---|---|
| npm package | 9.5 KB download, 30 KB unpacked, no dependencies | npm pack --dry-run |
| Start-up | 5.9 ms to load the module (most of it is node:crypto, for webhook signatures) + 0.05 ms for new StraitClient(...) | fresh Node 26 process on an M-series Mac, median of 25 |
Example app
examples/node creates a link, sends a purchase event and deletes the link, built against this repo's source (CI builds it on every PR).
npm cihere, thencd examples/node && npm ci.cp .env.example .envand set your secret key (st_live_…, a test workspace).npm start
Docs and support
- Docs: straitlink.in/docs/sdks/node/ · platform status · troubleshooting
- Talk to the Strait team: [email protected] (replies within 1 working day, IST) or call +91 81218 61890.
- Bugs and feature requests: open an issue on this repo.
- Security: never in a public issue. Write to [email protected] (see SECURITY.md).
