REST API · v1

The TRIMS
API Reference.

A predictable, resource-oriented REST API. JSON everywhere, bearer-token auth with granular scopes, and a machine-readable OpenAPI spec. Everything below maps one-to-one to a live endpoint under/api/v1.

Overview

Introduction

The TRIMS API lets you manage links, pull analytics, track conversions, and generate QR codes programmatically. It follows REST conventions: resource-based URLs, standard HTTP verbs, JSON request and response bodies, and conventional status codes.

  • All requests must be made over HTTPS.
  • All requests are authenticated with a workspace-scoped bearer token.
  • All responses are JSON, except binary endpoints like QR image export.
  • Every token action is logged and visible in your workspace API logs.
Availability
The developer API (tokens, webhooks, SDKs) is available on the . Free plans use the dashboard.
Overview

Base URL & versioning

All public API endpoints live under a single versioned base path on your TRIMS host:

Base URL
https://app.trims.app/api/v1

The version is pinned in the path (/v1). Backwards-incompatible changes ship under a new version; additive changes (new fields, new endpoints) may arrive within v1, so write clients that ignore unknown fields.

Self-hosting / custom domains
If you run TRIMS on your own domain, swap the host — the /api/v1 path is identical. During local development the base is http://localhost:8080/api/v1.
Overview

Authentication

Authenticate every request with a bearer token in the Authorization header. Create tokens in the dashboard under Settings → API tokens — you choose the workspace and the scopes each token carries.

Authorization header
Authorization: Bearer trims_9f8a7b6c5d4e3f2a1b0c8d7e6f5a4b3c
FieldTypeDescription
Authorization*headerBearer <token>. Tokens begin with the trims_ prefix and are bound to one workspace.
Content-Typeheaderapplication/json for POST/PATCH requests carrying a body.

A token resolves to its workspace automatically — you never pass a workspace id. Requests with a missing, malformed, or revoked token return 401; a valid token lacking the required scope returns 403.

Verify your token
curl https://app.trims.app/api/v1/links \
  -H "Authorization: Bearer $TRIMS_TOKEN"
Keep tokens server-side
Tokens are shown only once at creation and grant full scope-level access to a workspace. Never embed a live token in browser or mobile code. Rotate immediately if one leaks.
Overview

Scopes

Tokens are least-privilege by design. Each endpoint requires a specific scope; grant a token only the scopes its integration needs.

FieldTypeDescription
links.readscopeList and read links; generate QR codes.
links.writescopeCreate, update, and delete links.
tags.readscopeRead workspace tags.
domains.readscopeRead verified custom domains.
folders.readscopeRead folders.
analytics.readscopeRead analytics summaries and breakdowns.
conversions.readscopeList customers and conversion events.
conversions.writescopeRecord lead and sale events.
Insufficient scope
Calling an endpoint without its scope returns 403 with {"error":"Insufficient permissions. Required scope: <scope>"}.
Overview

Rate limits

Limits are enforced per workspace across all of its tokens. Exceeding a limit returns 429 with a Retry-After header (in seconds).

FieldTypeDescription
Pro1,200 / minStandard developer throughput.
Business3,000 / minFor higher-volume automation.
PremiumCustomNegotiated limits and dedicated capacity.
429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{ "error": "Rate limit exceeded" }
Be a good client
Batch where possible, cache reads, and honor Retry-After with exponential backoff rather than tight retry loops.
Overview

Errors

TRIMS uses conventional HTTP status codes. 2xx means success, 4xx indicates a client problem (the JSON body explains it), and 5xx indicates a server error.

FieldTypeDescription
200 / 201SuccessThe request succeeded (201 for resource creation).
400Bad RequestMalformed JSON or failed validation.
401UnauthorizedMissing, malformed, or revoked token.
403ForbiddenValid token, but missing scope or an upgrade-required plan gate.
404Not FoundResource doesn't exist or isn't in your workspace.
409ConflictDuplicate alias or domain.
429Too Many RequestsRate limit exceeded; retry after the given delay.
500Server ErrorSomething went wrong on our end — safe to retry.

Plan-gated errors

When an endpoint needs a higher plan, the response is a structured 403 your client can detect programmatically:

upgrade_required
{
  "error": "Advanced analytics require the Pro plan.",
  "code": "upgrade_required",
  "feature": "advanced_analytics",
  "requiredPlan": "pro"
}
Overview

Pagination

List endpoints are paginated with page and pageSize query parameters. pageSize defaults to 50 and is capped at 100.

Paginated list
curl "https://app.trims.app/api/v1/links?page=2&pageSize=50" \
  -H "Authorization: Bearer $TRIMS_TOKEN"
Paginated response shape
{
  "links": [ /* … */ ],
  "page": 2,
  "pageSize": 50,
  "total": 3842
}
Overview

OpenAPI spec

A machine-readable OpenAPI 3 document describes the entire public surface. Point Postman, Insomnia, or an OpenAPI code generator at it to scaffold a client in seconds.

GET/api/v1/openapi.json
Download the spec
curl https://app.trims.app/api/v1/openapi.json --output trims-openapi.json
Resources

Tags

Read the tags in your workspace to build filters or dashboards.

GET/api/v1/tags tags.read
200 response
{
  "tags": [
    { "id": "tag_1", "name": "campaign", "color": "#F3F1EC" },
    { "id": "tag_2", "name": "q4", "color": "#12B886" }
  ]
}
Resources

Domains

List the verified custom domains available for minting links in this workspace.

GET/api/v1/domains domains.read
200 response
{
  "domains": [
    { "id": "dom_1", "domain": "go.northlight.co", "verified": true, "ssl_status": "active" }
  ]
}
Resources

Folders

Folders group links for organization. Read them to power navigation or filtered creation.

GET/api/v1/folders folders.read
200 response
{
  "folders": [
    { "id": "fld_1", "name": "Campaigns", "linkCount": 42 }
  ]
}
Resources

Analytics

Read analytics for a link or your workspace. Requires the analytics.read scope (advanced metrics require the Pro plan).

GET/api/v1/analytics analytics.read

Pass an alias for a single link, or omit it for workspace-wide aggregates. Control the window with period (24h, 7d, 30d, 90d, all).

cURL
curl "https://app.trims.app/api/v1/analytics?alias=q4-launch&period=30d" \
  -H "Authorization: Bearer $TRIMS_TOKEN"
Example summary
{
  "total_clicks": 12483,
  "unique_visitors": 8121,
  "top_country": "US",
  "top_referrer": "linkedin.com",
  "period": "30d"
}
Deeper reports
Funnels, cohorts, retention, and ML insights are available in the dashboard analytics area. Raw click export is available via the analytics export in-app and to Pro tokens.
Resources

Conversion tracking

Record leads and sales so TRIMS can attribute revenue back to the link and customer that drove it. Requires conversions.write.

POST/api/v1/track/lead conversions.write
POST/api/v1/track/sale conversions.write
FieldTypeDescription
customer_id*stringThe customer the event belongs to (create/identify via your app).
event_namestringA label for the conversion, shown in reports.
amountintegerSale value in the smallest currency unit (e.g. cents). Sales only.
currencystringISO 4217 code. Defaults to usd. Sales only.
Track a sale
curl -X POST https://app.trims.app/api/v1/track/sale \
  -H "Authorization: Bearer $TRIMS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_8f2a",
    "amount": 4900,
    "currency": "usd",
    "event_name": "Pro subscription"
  }'
Resources

Customers

List the customers in your workspace with their attributed leads, sales, and lifetime revenue. Requires conversions.read.

GET/api/v1/customers conversions.read
200 response
{
  "customers": [
    {
      "id": "cus_8f2a",
      "email": "nadia@company.com",
      "first_link": "q4-launch",
      "leads": 3,
      "sales": 2,
      "revenue": 124000
    }
  ]
}
Resources

QR codes

Generate a QR code image for any URL. Requires links.read.

GET/api/v1/qr?url={url} links.read
Fetch a QR image
curl "https://app.trims.app/api/v1/qr?url=https://go.northlight.co/q4-launch" \
  -H "Authorization: Bearer $TRIMS_TOKEN" --output qr.png
Dynamic QR
For a link's dynamic QR (whose destination you can change later), generate the code for the link's short URL — scans are tracked as clicks automatically.
Advanced

OAuth 2.0

Building an app that acts on behalf of other TRIMS users? Use the OAuth 2.0 authorization-code flow instead of a static token, so each user grants your app scoped access to their workspace.

GET/oauth/authorize
POST/oauth/token
  1. 1Redirect the user to /oauth/authorize with your client_id, redirect_uri, response_type=code, and requested scopes.
  2. 2The user reviews the scopes and approves; TRIMS redirects back to your redirect_uri with a short-lived authorization code.
  3. 3Your server exchanges the code at /oauth/token for an access token and a refresh token.
  4. 4Call /api/v1/* with the access token; use the refresh token to obtain a new one when it expires.
Exchange the code
curl -X POST https://app.trims.app/oauth/token \
  -d grant_type=authorization_code \
  -d code=$AUTH_CODE \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d redirect_uri=$REDIRECT_URI
Advanced

Webhooks

Instead of polling, subscribe to events and TRIMS will POST a signed JSON payload to your endpoint whenever they occur. Manage webhook subscriptions in Settings → Webhooks (API access / Pro required).

Events

  • link.created, link.updated, link.deleted
  • link.clicked — emitted on every redirect (high volume).
  • lead.created, sale.created — conversion events.
  • spike.detected — anomalous-traffic alert.
Example delivery
{
  "event": "sale.created",
  "created_at": "2026-03-02T12:04:11Z",
  "data": {
    "customer_id": "cus_8f2a",
    "link": "q4-launch",
    "amount": 4900,
    "currency": "usd"
  }
}

Verifying signatures

Each delivery includes an X-Trims-Signature header — an HMAC-SHA256 of the raw request body using your webhook secret. Recompute it and compare in constant time to reject spoofed or tampered requests.

Verify (Node.js)
import crypto from "node:crypto";

export function verify(rawBody, signature, secret) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Respond fast
Return a 2xx within a few seconds. Do heavy work asynchronously — TRIMS retries failed deliveries with backoff and records every attempt.
Advanced

SDKs & tooling

Use the raw REST API from any language, or generate a typed client from the OpenAPI spec.

  • Point Postman or Insomnia at https://app.trims.app/api/v1/openapi.json to import every endpoint.
  • Run openapi-generator to scaffold a client in your language of choice.
  • Store your token in an environment variable (TRIMS_TOKEN) — never in source control.
Generate a typed client
npx @openapitools/openapi-generator-cli generate \
  -i https://app.trims.app/api/v1/openapi.json \
  -g typescript-fetch \
  -o ./trims-client
Advanced

Changelog

v1 · current
Links, tags, domains, folders, analytics, conversion tracking, customers, QR, OpenAPI spec, OAuth 2.0, and webhooks.
Compatibility
Additive changes (new fields/endpoints) ship within v1. Breaking changes will be released under a new version path.
Advanced

Support

Stuck on an integration? We're happy to help.

Build something great

Grab a scoped token from your workspace settings and make your first API call in minutes.