Trillboards API Error Reference

Every external Trillboards API endpoint returns errors using the canonical envelope below. Phase 1 of the API DX overhaul locks this contract; Phase 2+ migrates each remaining surface onto it.


{
  "error": {
    "type": "invalid_request_error",
    "code": "screen_not_found",
    "message": "Screen 'scr_xxx' not found or access denied.",
    "param": "screen_id",
    "doc_url": "https://api.trillboards.com/docs/errors#screen_not_found",
    "request_id": "req_a1b2c3d4e5f6789012345678901234ab",
    "details": [
      { "param": "screens[0].orientation", "code": "invalid_enum", "message": "Must be 'landscape' or 'portrait'." }
    ],
    "help": {
      "summary": "Screen IDs are prefixed 'scr_'. List your screens at GET /v1/partner/screens.",
      "suggestions": ["scr_abc123", "scr_def456"],
      "portal_url": "https://trillboards.com/earner/screens",
      "try_next": "/api/discover?q=screen"
    }
  }
}

Field rules

HTTP status codes never change as part of the body migration. Callers can opt back into the prior flat envelope { statusCode, message, code, param, request_id } by sending the header X-Error-Shape: legacy. This opt-in is removed two weeks after Phase 1 ships.


Authentication Errors

AUTH_REQUIRED

authentication_required


{ "error": { "type": "authentication_error", "code": "authentication_required", "message": "Authentication required.", "param": null, "doc_url": "https://api.trillboards.com/docs/errors#authentication_required", "request_id": "req_…" } }

How to fix: include Authorization: Bearer <key> on the request.

invalid_api_key

How to fix: rotate the key in the portal and retry with the fresh value.

invalid_api_key_format

How to fix: Partner keys begin with trb_partner_. Make sure you copied the full key, including the prefix.

api_key_expired

How to fix: rotate the key in the partner portal.

api_key_revoked

How to fix: issue a new key in the portal.

auth_error

How to fix: verify credentials and retry. Contact support with the request_id if the problem persists.


Permission Errors

CREATIVE_ADVERTISEMENT_NOT_OWNED

creative_policy_forbidden

insufficient_scope

How to fix: inspect help.required_scope and issue a key with the needed scopes.

insufficient_permissions

How to fix: contact an account owner to grant access, or use a different key.

partner_inactive

How to fix: contact support to reactivate the partner account.

forbidden

How to fix: verify the resource belongs to the authenticated caller.

tier_insufficient

How to fix: upgrade the data-access tier. help.required_tier indicates the minimum tier needed.


Validation & Request Errors

INVALID_FEEDBACK_CREATIVE

SOURCE_CREATIVE_NOT_FOUND

UNSUPPORTED_CREATIVE_TYPE

INVALID_CREATIVE_URL

CREATIVE_TOO_LARGE

INVALID_FEEDBACK_REQUEST

EXPERIMENT_NOT_FOUND

INVALID_FEEDBACK_OBJECTIVE

MEDIA_BUY_NOT_FOUND

INVALID_POLICY_VERSION

INVALID_CREATIVE_WEIGHTS

INVALID_VARIANTS

BRAND_PROFILE_NOT_FOUND

INVALID_BRAND_RULES

invalid_creative_policy

validation_error


{ "error": { "type": "invalid_request_error", "code": "validation_error", "message": "2 validation errors occurred.", "param": "email", "details": [ { "param": "email", "code": "invalid_string", "message": "Invalid email" }, { "param": "age", "code": "too_small", "message": "Must be >= 18" } ], "doc_url": "https://api.trillboards.com/docs/errors#validation_error", "request_id": "req_…" } }

How to fix: inspect every entry in details[] and resubmit with corrected values.

not_found

How to fix: list resources first to find a valid ID.

IDEMPOTENCY_CONFLICT

CREATIVE_POLICY_CONFLICT

EXPERIMENT_COMPLETED

CREATIVE_NOT_READY

VARIANT_IDENTITY_CONFLICT

OPERATION_IN_PROGRESS

OPERATION_ALREADY_COMMITTED

BRAND_PROFILE_CONFLICT

CREATIVE_NOT_LINKED

creative_policy_conflict

FEEDBACK_POLICY_REQUIRED

conflict

How to fix: re-fetch the resource and retry with the latest version.

missing_required_field

How to fix: inspect param and include the missing field in the request body.

invalid_field_value

How to fix: consult the OpenAPI schema for the accepted set of values.

invalid_field_type

How to fix: ensure the field type matches the schema (string/number/boolean/array).

preferences_must_be_object

How to fix: send preferences as a JSON object ({ ... }), not an array, string, or number.

field_must_be_boolean

How to fix: pass the field as a JSON boolean, not as "true"/"false" strings or 0/1.

field_must_be_array

How to fix: wrap the value in square brackets — [value] even for a single entry.

array_items_must_be_strings

How to fix: cast numeric or object elements to strings before sending, or remove them.

lbar_settings_must_be_object

How to fix: send lbar_settings as a JSON object ({ ... }), or omit the field to use defaults.

pip_settings_must_be_object

How to fix: send pip_settings as a JSON object ({ ... }), or omit the field to use defaults.

invalid_json

How to fix: send a JSON body and set Content-Type: application/json.

field_too_long

How to fix: shorten the offending field.

array_too_large

How to fix: split the request into multiple batches.

invalid_array_items

How to fix: inspect each item and ensure all match the schema.

conflicting_lists

How to fix: remove the duplicate entry from one of the lists.

screen_not_found

How to fix: call GET /v1/partner/screens to list valid screen IDs.

invalid_screen_id

How to fix: screen IDs are alphanumeric strings — verify you are not prepending prefixes or trailing spaces.

preset_not_found

How to fix: list available presets via GET /v1/partner/content-presets.

preset_id_required

How to fix: supply a valid preset_id in the request body.

preferences_required

How to fix: include one of the two fields in the request body.

screen_ids_required

How to fix: pass at least one screen ID.

batch_size_exceeded

How to fix: split the request into smaller batches.

invalid_preferences

How to fix: supply preferences as a JSON object.

invalid_preference_level

How to fix: use one of the supported preference levels.

invalid_age_rating

How to fix: use one of the supported age ratings.

invalid_display_mode

How to fix: use one of the supported display modes.

invalid_orientation

How to fix: use landscape or portrait.

invalid_sizing_mode

How to fix: use one of the supported sizing modes.

invalid_lbar_position

How to fix: choose one of the supported l-bar positions.

invalid_pip_position

How to fix: choose one of the supported picture-in-picture positions.

invalid_ad_surface_mode

How to fix: choose one of the supported ad-surface modes.

ad_interval_out_of_range

How to fix: clamp the interval to the documented min/max.

lbar_size_out_of_range

How to fix: clamp the size to the documented min/max.

pip_size_ratio_out_of_range

How to fix: clamp the ratio to the documented min/max.

pip_margin_out_of_range

How to fix: clamp the margin to the documented min/max.

invalid_lbar_settings

How to fix: pass l_bar as a JSON object.

invalid_pip_settings

How to fix: pass pip as a JSON object.

invalid_currency

How to fix: use a supported ISO-4217 currency code.

invalid_provider

How to fix: choose one of the supported header-bidding providers.

floor_price_out_of_range

How to fix: clamp floor_price_cpm to the documented min/max.

allocation_out_of_range

How to fix: clamp the value to the documented min/max.

timeout_out_of_range

How to fix: clamp timeout_ms to the documented min/max.

blocked_advertisers_too_large

How to fix: split the blocklist or remove stale entries.

blocked_iab_categories_too_large

How to fix: split the blocklist or remove stale entries.

webhook_not_found

How to fix: list webhooks and confirm the ID.

invalid_webhook_url

How to fix: supply an https:// URL.

webhook_url_unreachable

How to fix: confirm DNS, TLS, and that the target returns 2xx on a health probe.

invalid_webhook_events

How to fix: consult the webhook documentation for supported event types.

webhook_limit_exceeded

How to fix: delete unused webhooks before creating new ones.

webhook_secret_expired

How to fix: rotate the signing secret.

device_not_found

How to fix: list devices first to find a valid ID.

invalid_device_id

How to fix: confirm the device ID format with documentation.

device_already_registered

How to fix: unbind the device from its current screen before re-registering, or register a new device.

invalid_fingerprint

How to fix: confirm the fingerprint format with documentation.

stream_not_found

How to fix: list streams first to find a valid ID.

invalid_stream_type

How to fix: use a supported stream type.

stream_content_required

How to fix: supply at least one content field.

invalid_media_url

How to fix: supply a valid absolute URL.

insufficient_data

How to fix: retry after the screen has accumulated more impressions.

segment_not_found

How to fix: list segments first to find a valid ID.

segment_limit_exceeded

How to fix: delete unused segments before creating a new one.

invalid_segment_rules

How to fix: every rule must have field, operator, and value.


Rate Limiting

rate_limit_exceeded

Rate-limit responses also set the Retry-After header.

How to fix: back off for the seconds indicated by Retry-After, then retry.

daily_quota_exceeded

How to fix: wait until the next UTC day or upgrade the plan.

concurrent_request_limit

How to fix: reduce request concurrency.


Concurrency & Idempotency

resource_locked

How to fix: retry after the in-flight request completes.

stale_data

How to fix: re-fetch the resource and resend the update with the new version.

idempotency_key_in_use

How to fix: retry with the same key once the previous request completes, or use a fresh key.

idempotency_key_mismatch

How to fix: either match the original body or use a fresh idempotency key.

idempotency_error

How to fix: consult the more specific idempotency_* codes and retry.


API Errors

internal_error

The message never leaks server internals. Always include the request_id when filing a support ticket.

How to fix: retry after a backoff. If the issue persists, contact support with the request_id.

database_error

How to fix: retry after a backoff.

service_unavailable

How to fix: retry after a short backoff.

CREATIVE_MODERATION_UNAVAILABLE

POLICY_NOT_AVAILABLE

BRAND_CONFIGURATION_FAILED

device_offline

How to fix: retry once the device reconnects, or send the command via a queueable endpoint.

analytics_unavailable

How to fix: retry after a short backoff.

timeout

How to fix: retry. If the problem persists, contact support with the request_id.

error

How to fix: fall back to retry with backoff; escalate with request_id if the issue is persistent.

device_identity_underivable

How to fix: check param (a comma-separated list of the fields that could not be resolved, e.g. make,model), then either send a real browser/WebView User-Agent string in ua, or supply hardware.make / hardware.model / hardware.os explicitly to bypass UA parsing entirely.


Data API

Errors specific to the /v1/data/* surface (Phase 2 migration).

screen_not_in_scope

How to fix: use a screen whose ID is listed in your API key's scoped_screen_ids, or issue a new key without screen-level scoping. GET /v1/data/screens returns the full set of screens the current key can see.

dataset_not_scopable

How to fix: the dataset (for example creative_venue_performance) is aggregated at venue granularity and cannot be restricted by screen. Either remove screen-level scoping from the key or query a dataset with a screen_mongo_id column.

invalid_dataset

How to fix: read help.allowed on the error payload for the list of valid dataset names.

invalid_dimensions

How to fix: help.allowed lists the dimensions defined on the dataset; use only those.

invalid_measures

How to fix: help.allowed lists the measures defined on the dataset; use only those.

invalid_order_by

How to fix: every order_by entry must reference a field that is also listed in dimensions or measures.


SDK API

Errors specific to the /v2/sdk/* measurement surface (Phase 4 migration).

client_ip_required

How to fix: the SDK derives household identity and geolocation from the client IP. Set X-Forwarded-For (via an upstream proxy) or send the request from a direct connection so getClientIP(req) can resolve a valid value.

api_key_owner_missing

How to fix: rotate the key in the portal. Missing owner IDs usually indicate a test key that was created without a partner row and should be replaced with a production key.

sdk_enrich_rate_limit_exceeded

How to fix: batch enrichment client-side and back off for Retry-After seconds. Cache the returned segments locally for the household lifetime of the request IP (24h TTL server-side).

sdk_qr_scan_rate_limit_exceeded

How to fix: a QR campaign should not produce more than ~100 scans per minute per publisher — if you are seeing this frequently, investigate whether the same QR is being mis-scanned by a crawler and coalesce client side before posting.

sdk_qr_beacon_rate_limit_exceeded

How to fix: beacons should fire once per scan. Back off for Retry-After seconds; if the limit is hit at steady state, request a quota lift via the portal.

sdk_impression_rate_limit_exceeded

How to fix: coalesce impressions client-side or split traffic across multiple API keys with per-publisher scoping.


Device API

Errors specific to the /v2/device/* managed-device control surface (Phase 4 migration).

unknown_agent_type

How to fix: omit the agent query parameter to receive the full manifest bundle, or supply one of the documented agent types (agent-core, agent-core-lite, tablet-agent, android-tv-agent).


Sensing API

Errors specific to the /v2/sensing/* paired-training surface (Phase 4 migration).

paired_training_rate_limit_exceeded

How to fix: the collector should batch ~500 samples per call and space uploads across minutes. Back off for Retry-After seconds before the next attempt.

device_identity_missing

How to fix: ensure the device-token middleware is configured correctly. If deviceAuth.deviceId is null, the resolved device has no canonical identity and cannot be attributed — contact support with the request_id.

paired_training_insert_failed

How to fix: retry with backoff — the endpoint is idempotent on sample_id so duplicate uploads from a previously partially-written batch are silently deduped.

OpenRTB / DSP

openrtb_auth_failed

How to fix: supply a valid API key via the x-api-key header or Authorization: Bearer <key>. Keys are provisioned at DSP onboarding.

openrtb_config_missing

How to fix: this is a server configuration issue. Contact support with the request_id from the response.

unsupported_media_type

How to fix: add the Content-Type: application/json header to your request.

dsp_access_denied

How to fix: contact support@trillboards.com to resolve your account status.

204 No Content (no-fill)

The OpenRTB surface returns HTTP 204 No Content (empty body) when no ad fill is available. This is canonical OpenRTB behavior and is NOT an error. Callers must not treat 204 as a failure — it means no matching inventory was available for the bid request parameters at the time of the request.