Trillboards
Back to Developers
Developer Docs

Idempotency & Safety.

Safe-by-default APIs for automation, AI agents, and retry-heavy integrations.

Overview

Every mutating endpoint on the Trillboards API supports idempotency keys, dry-run validation, and request replay. These features make it safe to build retry logic, AI agent integrations, and automated pipelines that never accidentally create duplicate resources or double-charge.

Idempotency Keys

Pass an Idempotency-Key header on any POST/PUT/PATCH. The server returns the cached response for duplicate keys within a 24-hour window.

Dry-Run Validation

Add ?dry_run=true to any mutating request. The server validates inputs, checks permissions, and returns what would happen β€” without persisting anything.

Request Replay

Replay any previous request by ID. The server returns the original response, letting you recover from network failures without side effects.

AI-Agent Affordances

Structured error responses with machine-readable codes, docs URLs, and suggested fixes β€” designed for LLM tool-use loops.

Idempotency Keys

Include an Idempotency-Key header on any POST, PUT, or PATCH request. If the server has already processed a request with the same key, it returns the cached response immediately.

idempotent-request.ts
import { TrillboardsClient } from '@trillboards/api-client';

const client = new TrillboardsClient({ apiKey: process.env.TRILLBOARDS_API_KEY });

// The SDK generates idempotency keys automatically for mutations
const screen = await client.screens.create({
  name: 'Lobby Display',
  deviceId: 'fp_abc123',
}, {
  idempotencyKey: 'create-lobby-display-2026-04-16',
});

// Safe to retry β€” same key returns cached response
const retry = await client.screens.create({
  name: 'Lobby Display',
  deviceId: 'fp_abc123',
}, {
  idempotencyKey: 'create-lobby-display-2026-04-16',
});

// screen.id === retry.id (no duplicate created)

cURL Example

Terminal
$ curl -X POST https://api.trillboards.com/v1/partner/devices \
  -H "Authorization: Bearer trb_partner_xxx" \
  -H "Idempotency-Key: register-device-001" \
  -H "Content-Type: application/json" \
  -d '{"device_id": "my-kiosk-001", "device_type": "kiosk"}'

# Retry with same key β€” returns cached 201, no duplicate
$ curl -X POST https://api.trillboards.com/v1/partner/devices \
  -H "Authorization: Bearer trb_partner_xxx" \
  -H "Idempotency-Key: register-device-001" \
  -H "Content-Type: application/json" \
  -d '{"device_id": "my-kiosk-001", "device_type": "kiosk"}'
  • Keys are scoped to your API key β€” two partners can use the same key without collision
  • Keys expire after 24 hours β€” after expiration, the same key can be reused
  • Keys are case-sensitive and can be up to 256 characters
  • The SDK auto-generates UUIDv4 keys when you don't provide one

Dry-Run Validation

Add the dry_run query parameter to any mutating request. The server runs the full validation pipeline β€” schema validation, permission checks, rate limits, uniqueness constraints β€” and returns what would happen, without persisting anything.

dry-run.ts
// Validate a screen creation without actually creating it
const result = await client.screens.create({
  name: 'Lobby Display',
  deviceId: 'fp_abc123',
}, {
  dryRun: true,
});

// result.dryRun === true
// result.data contains the screen object that WOULD be created
// result.warnings contains any non-blocking issues
// Nothing was persisted β€” no screen exists
Terminal
$ curl -X POST "https://api.trillboards.com/v1/partner/devices?dry_run=true" \
  -H "Authorization: Bearer trb_partner_xxx" \
  -H "Content-Type: application/json" \
  -d '{"device_id": "my-kiosk-001", "device_type": "kiosk"}'

# Response includes { "dry_run": true, "data": { ... }, "warnings": [] }

AI-Agent Affordances

Every error response is structured for programmatic consumption by AI agents and LLM tool-use loops. Errors include machine-readable codes, documentation URLs, and suggested fixes.

error-response.json
{
  "error": {
    "code": "SCREEN_NOT_FOUND",
    "type": "validation",
    "message": "Screen scr_xyz does not exist or is not accessible",
    "request_id": "req_abc123",
    "docs_url": "https://api.trillboards.com/docs/errors#SCREEN_NOT_FOUND",
    "suggestion": "Check the screen ID. Use GET /v1/partner/screens to list available screens.",
    "related_endpoints": [
      "GET /v1/partner/screens",
      "GET /v1/partner/screens/:id"
    ]
  }
}
  • Every error has a unique, stable .code that AI agents can match on
  • The .docs_url links directly to the error's documentation page
  • The .suggestion field provides a human-readable fix the agent can relay to the user
  • The .related_endpoints field lists other endpoints the agent can call to recover
  • Rate limit errors include Retry-After headers with exact wait times
  • All responses include X-Request-Id for end-to-end tracing

Machine-Readable API Spec

The full OpenAPI spec is available at api.trillboards.com/docs for tool-use agents. An LLM-optimized summary is at api.trillboards.com/llms.txt for AI coding assistants.

Need Help?

Contact our developer support at developers@trillboards.com or visit the Support page.

Build with confidence.

Safe-by-default APIs that never double-charge, never duplicate, and always explain what went wrong.