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
typeis one ofinvalid_request_error(4xx client),authentication_error(401),permission_error(403),rate_limit_error(429),idempotency_error(409),api_error(5xx).codeis stable and machine-readable; it never changes across versions.detailsis present only when multiple field-level violations exist (e.g. a Zod validation result with more than one issue).helpis present only when we have structured remediation context.request_idmatches theX-Request-Idresponse header and is always set.doc_urlis always computable ashttps://api.trillboards.com/docs/errors#<code>.
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
- Type:
authentication_error - HTTP status: 401
- Summary: Connect an authenticated native buying account.
authentication_required
- Type:
authentication_error - HTTP status: 401
- Summary: No API key or access token was supplied.
{ "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
- Type:
authentication_error - HTTP status: 401
- Summary: The API key was not recognised or is inactive.
How to fix: rotate the key in the portal and retry with the fresh value.
invalid_api_key_format
- Type:
authentication_error - HTTP status: 401
- Summary: The API key does not match the expected format.
How to fix: Partner keys begin with trb_partner_. Make sure you copied the full key, including the prefix.
api_key_expired
- Type:
authentication_error - HTTP status: 401
- Summary: The API key has expired and must be rotated.
How to fix: rotate the key in the partner portal.
api_key_revoked
- Type:
authentication_error - HTTP status: 401
- Summary: The API key has been revoked and can no longer be used.
How to fix: issue a new key in the portal.
auth_error
- Type:
authentication_error - HTTP status: 401
- Summary: Generic authentication failure.
How to fix: verify credentials and retry. Contact support with the request_id if the problem persists.
Permission Errors
CREATIVE_ADVERTISEMENT_NOT_OWNED
- Type:
permission_error - HTTP status: 403
- Summary: The native advertisement is not owned by the resolved advertiser.
creative_policy_forbidden
- Type:
permission_error - HTTP status: 403
- Summary: The native creative policy belongs to another advertiser.
insufficient_scope
- Type:
permission_error - HTTP status: 403
- Summary: The API key is valid but lacks the scope required for this endpoint.
How to fix: inspect help.required_scope and issue a key with the needed scopes.
insufficient_permissions
- Type:
permission_error - HTTP status: 403
- Summary: The caller does not have permission to perform this action.
How to fix: contact an account owner to grant access, or use a different key.
partner_inactive
- Type:
permission_error - HTTP status: 403
- Summary: The partner account is not currently active.
How to fix: contact support to reactivate the partner account.
forbidden
- Type:
permission_error - HTTP status: 403
- Summary: Access denied.
How to fix: verify the resource belongs to the authenticated caller.
tier_insufficient
- Type:
permission_error - HTTP status: 403
- Summary: This endpoint requires a higher data-access tier than the caller has.
How to fix: upgrade the data-access tier. help.required_tier indicates the minimum tier needed.
Validation & Request Errors
INVALID_FEEDBACK_CREATIVE
- Type:
invalid_request_error - HTTP status: 400
- Summary: Supply exactly one valid HTTPS URL, owned creative ID or base64 image with the native creative fields.
SOURCE_CREATIVE_NOT_FOUND
- Type:
invalid_request_error - HTTP status: 404
- Summary: The source creative ID has no asset in this buyer library.
UNSUPPORTED_CREATIVE_TYPE
- Type:
invalid_request_error - HTTP status: 400
- Summary: The actual creative bytes or declared media type are unsupported for this native import or serving path.
INVALID_CREATIVE_URL
- Type:
invalid_request_error - HTTP status: 400
- Summary: The creative download URL must use HTTPS and resolve to a public destination.
CREATIVE_TOO_LARGE
- Type:
invalid_request_error - HTTP status: 400
- Summary: The creative exceeds the existing native media byte budget.
INVALID_FEEDBACK_REQUEST
- Type:
invalid_request_error - HTTP status: 400
- Summary: A required native creative-feedback field is invalid.
EXPERIMENT_NOT_FOUND
- Type:
invalid_request_error - HTTP status: 404
- Summary: The native experiment is absent or belongs to another buyer.
INVALID_FEEDBACK_OBJECTIVE
- Type:
invalid_request_error - HTTP status: 400
- Summary: Choose gaze_share, gaze_overlap_ms or dwell_ms.
MEDIA_BUY_NOT_FOUND
- Type:
invalid_request_error - HTTP status: 404
- Summary: The native media package is absent or belongs to another buyer.
INVALID_POLICY_VERSION
- Type:
invalid_request_error - HTTP status: 400
- Summary: Supply an exact nonnegative decimal BIGINT policy version string.
INVALID_CREATIVE_WEIGHTS
- Type:
invalid_request_error - HTTP status: 400
- Summary: Use unique registered creatives with finite weights 0–1 and at least one positive weight.
INVALID_VARIANTS
- Type:
invalid_request_error - HTTP status: 400
- Summary: Use 1–100 unique native variant and creative identities.
BRAND_PROFILE_NOT_FOUND
- Type:
invalid_request_error - HTTP status: 404
- Summary: The native brand profile is absent or belongs to another buyer.
INVALID_BRAND_RULES
- Type:
invalid_request_error - HTTP status: 400
- Summary: The brand rules do not satisfy the existing native BrandRulesSchema.
invalid_creative_policy
- Type:
invalid_request_error - HTTP status: 400
- Summary: The native creative policy owner, mode, version or weight generation is invalid.
validation_error
- Type:
invalid_request_error - HTTP status: 400
- Summary: Request data failed validation. See
details[]for per-field violations.
{ "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
- Type:
invalid_request_error - HTTP status: 404
- Summary: The requested resource does not exist or is not visible to the caller.
How to fix: list resources first to find a valid ID.
IDEMPOTENCY_CONFLICT
- Type:
idempotency_error - HTTP status: 409
- Summary: The enrollment key belongs to different arguments.
CREATIVE_POLICY_CONFLICT
- Type:
idempotency_error - HTTP status: 409
- Summary: Refresh the native policy: its version or owner changed.
EXPERIMENT_COMPLETED
- Type:
idempotency_error - HTTP status: 409
- Summary: The native creative experiment has stopped.
CREATIVE_NOT_READY
- Type:
idempotency_error - HTTP status: 409
- Summary: The native creative must be brand approved, moderated and linked to the funded advertisement.
VARIANT_IDENTITY_CONFLICT
- Type:
idempotency_error - HTTP status: 409
- Summary: An existing variant keeps its creative bytes and model identity.
OPERATION_IN_PROGRESS
- Type:
idempotency_error - HTTP status: 409
- Summary: The metered native operation is still in progress; do not execute it again under another key.
OPERATION_ALREADY_COMMITTED
- Type:
idempotency_error - HTTP status: 409
- Summary: The metered native operation already committed; its existing usage receipt is returned in help.usage_receipt.
BRAND_PROFILE_CONFLICT
- Type:
idempotency_error - HTTP status: 409
- Summary: The supplied native brand profile belongs to another campaign or owner scope.
CREATIVE_NOT_LINKED
- Type:
idempotency_error - HTTP status: 409
- Summary: Explicit brand approval did not link the creative into the enabled native campaign.
creative_policy_conflict
- Type:
idempotency_error - HTTP status: 409
- Summary: The native creative policy version, mode or integration owner changed.
FEEDBACK_POLICY_REQUIRED
- Type:
idempotency_error - HTTP status: 409
- Summary: Use the versioned partner creative-feedback methods for this funded experiment.
conflict
- Type:
idempotency_error - HTTP status: 409
- Summary: The requested change conflicts with the current state of the resource.
How to fix: re-fetch the resource and retry with the latest version.
missing_required_field
- Type:
invalid_request_error - HTTP status: 400
- Summary: A required field was not supplied.
How to fix: inspect param and include the missing field in the request body.
invalid_field_value
- Type:
invalid_request_error - HTTP status: 400
- Summary: A field was supplied with an invalid value.
How to fix: consult the OpenAPI schema for the accepted set of values.
invalid_field_type
- Type:
invalid_request_error - HTTP status: 400
- Summary: A field had the wrong JSON type.
How to fix: ensure the field type matches the schema (string/number/boolean/array).
preferences_must_be_object
- Type:
invalid_request_error - HTTP status: 400
- Summary: The
preferencesfield must be a JSON object (got a non-object type).
How to fix: send preferences as a JSON object ({ ... }), not an array, string, or number.
field_must_be_boolean
- Type:
invalid_request_error - HTTP status: 400
- Summary: The named field must be a boolean
trueorfalse.
How to fix: pass the field as a JSON boolean, not as "true"/"false" strings or 0/1.
field_must_be_array
- Type:
invalid_request_error - HTTP status: 400
- Summary: The named field must be an array.
How to fix: wrap the value in square brackets — [value] even for a single entry.
array_items_must_be_strings
- Type:
invalid_request_error - HTTP status: 400
- Summary: Every element of the named array must be a string.
How to fix: cast numeric or object elements to strings before sending, or remove them.
lbar_settings_must_be_object
- Type:
invalid_request_error - HTTP status: 400
- Summary: The
lbar_settingsfield must be a JSON object.
How to fix: send lbar_settings as a JSON object ({ ... }), or omit the field to use defaults.
pip_settings_must_be_object
- Type:
invalid_request_error - HTTP status: 400
- Summary: The
pip_settingsfield must be a JSON object.
How to fix: send pip_settings as a JSON object ({ ... }), or omit the field to use defaults.
invalid_json
- Type:
invalid_request_error - HTTP status: 400
- Summary: The request body was not valid JSON.
How to fix: send a JSON body and set Content-Type: application/json.
field_too_long
- Type:
invalid_request_error - HTTP status: 400
- Summary: A string field exceeded its maximum length.
How to fix: shorten the offending field.
array_too_large
- Type:
invalid_request_error - HTTP status: 400
- Summary: An array field exceeded the maximum number of allowed items.
How to fix: split the request into multiple batches.
invalid_array_items
- Type:
invalid_request_error - HTTP status: 400
- Summary: One or more items in an array had an invalid type or value.
How to fix: inspect each item and ensure all match the schema.
conflicting_lists
- Type:
invalid_request_error - HTTP status: 400
- Summary: The same value appeared in both an allow list and a block list.
How to fix: remove the duplicate entry from one of the lists.
screen_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Screen not found or access denied.
How to fix: call GET /v1/partner/screens to list valid screen IDs.
invalid_screen_id
- Type:
invalid_request_error - HTTP status: 400
- Summary: Screen ID is not in the expected format.
How to fix: screen IDs are alphanumeric strings — verify you are not prepending prefixes or trailing spaces.
preset_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Content preset not found.
How to fix: list available presets via GET /v1/partner/content-presets.
preset_id_required
- Type:
invalid_request_error - HTTP status: 400
- Summary:
preset_idis required.
How to fix: supply a valid preset_id in the request body.
preferences_required
- Type:
invalid_request_error - HTTP status: 400
- Summary: Either
preferencesorpreset_idis required.
How to fix: include one of the two fields in the request body.
screen_ids_required
- Type:
invalid_request_error - HTTP status: 400
- Summary:
screen_idsmust be a non-empty array.
How to fix: pass at least one screen ID.
batch_size_exceeded
- Type:
invalid_request_error - HTTP status: 400
- Summary: More screens were sent in one request than the batch limit allows.
How to fix: split the request into smaller batches.
invalid_preferences
- Type:
invalid_request_error - HTTP status: 400
- Summary:
preferencesmust be an object.
How to fix: supply preferences as a JSON object.
invalid_preference_level
- Type:
invalid_request_error - HTTP status: 400
- Summary:
preference_levelis not one of the allowed values.
How to fix: use one of the supported preference levels.
invalid_age_rating
- Type:
invalid_request_error - HTTP status: 400
- Summary:
max_age_ratingis not one of the allowed values.
How to fix: use one of the supported age ratings.
invalid_display_mode
- Type:
invalid_request_error - HTTP status: 400
- Summary:
display_modeis not one of the allowed values.
How to fix: use one of the supported display modes.
invalid_orientation
- Type:
invalid_request_error - HTTP status: 400
- Summary:
screen_orientationis not one of the allowed values.
How to fix: use landscape or portrait.
invalid_sizing_mode
- Type:
invalid_request_error - HTTP status: 400
- Summary:
advertisement_sizingis not one of the allowed values.
How to fix: use one of the supported sizing modes.
invalid_lbar_position
- Type:
invalid_request_error - HTTP status: 400
- Summary:
l_bar.positionis not one of the allowed values.
How to fix: choose one of the supported l-bar positions.
invalid_pip_position
- Type:
invalid_request_error - HTTP status: 400
- Summary:
pip.positionis not one of the allowed values.
How to fix: choose one of the supported picture-in-picture positions.
invalid_ad_surface_mode
- Type:
invalid_request_error - HTTP status: 400
- Summary:
ad_surface_modeis not one of the allowed values.
How to fix: choose one of the supported ad-surface modes.
ad_interval_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
ad_interval_secondsis outside the supported range.
How to fix: clamp the interval to the documented min/max.
lbar_size_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
l_bar.size_percentis outside the supported range.
How to fix: clamp the size to the documented min/max.
pip_size_ratio_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
pip.size_ratiois outside the supported range.
How to fix: clamp the ratio to the documented min/max.
pip_margin_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
pip.margin_pxis outside the supported range.
How to fix: clamp the margin to the documented min/max.
invalid_lbar_settings
- Type:
invalid_request_error - HTTP status: 400
- Summary:
l_barmust be an object.
How to fix: pass l_bar as a JSON object.
invalid_pip_settings
- Type:
invalid_request_error - HTTP status: 400
- Summary:
pipmust be an object.
How to fix: pass pip as a JSON object.
invalid_currency
- Type:
invalid_request_error - HTTP status: 400
- Summary:
currencyis not one of the allowed values.
How to fix: use a supported ISO-4217 currency code.
invalid_provider
- Type:
invalid_request_error - HTTP status: 400
- Summary:
provideris not one of the allowed values.
How to fix: choose one of the supported header-bidding providers.
floor_price_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
floor_price_cpmis outside the supported range.
How to fix: clamp floor_price_cpm to the documented min/max.
allocation_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
programmatic_allocation_percentis outside the supported range.
How to fix: clamp the value to the documented min/max.
timeout_out_of_range
- Type:
invalid_request_error - HTTP status: 400
- Summary:
timeout_msis outside the supported range.
How to fix: clamp timeout_ms to the documented min/max.
blocked_advertisers_too_large
- Type:
invalid_request_error - HTTP status: 400
- Summary: Too many blocked advertisers.
How to fix: split the blocklist or remove stale entries.
blocked_iab_categories_too_large
- Type:
invalid_request_error - HTTP status: 400
- Summary: Too many blocked IAB categories.
How to fix: split the blocklist or remove stale entries.
webhook_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Webhook endpoint not found.
How to fix: list webhooks and confirm the ID.
invalid_webhook_url
- Type:
invalid_request_error - HTTP status: 400
- Summary: The webhook URL must be a valid HTTPS URL.
How to fix: supply an https:// URL.
webhook_url_unreachable
- Type:
invalid_request_error - HTTP status: 400
- Summary: The supplied webhook URL did not respond.
How to fix: confirm DNS, TLS, and that the target returns 2xx on a health probe.
invalid_webhook_events
- Type:
invalid_request_error - HTTP status: 400
- Summary: The supplied webhook event list contains unsupported values.
How to fix: consult the webhook documentation for supported event types.
webhook_limit_exceeded
- Type:
invalid_request_error - HTTP status: 400
- Summary: The maximum number of webhooks for this partner has been reached.
How to fix: delete unused webhooks before creating new ones.
webhook_secret_expired
- Type:
invalid_request_error - HTTP status: 400
- Summary: The webhook signing secret has expired.
How to fix: rotate the signing secret.
device_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Device not found or access denied.
How to fix: list devices first to find a valid ID.
invalid_device_id
- Type:
invalid_request_error - HTTP status: 400
- Summary: Device ID is not in the expected format.
How to fix: confirm the device ID format with documentation.
device_already_registered
- Type:
idempotency_error - HTTP status: 409
- Summary: The device is already bound to another screen.
How to fix: unbind the device from its current screen before re-registering, or register a new device.
invalid_fingerprint
- Type:
invalid_request_error - HTTP status: 400
- Summary: Device fingerprint is not in the expected format.
How to fix: confirm the fingerprint format with documentation.
stream_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Stream not found or access denied.
How to fix: list streams first to find a valid ID.
invalid_stream_type
- Type:
invalid_request_error - HTTP status: 400
- Summary: Stream type is not one of the allowed values.
How to fix: use a supported stream type.
stream_content_required
- Type:
invalid_request_error - HTTP status: 400
- Summary: Stream content (video URL, images, etc.) must be provided.
How to fix: supply at least one content field.
invalid_media_url
- Type:
invalid_request_error - HTTP status: 400
- Summary: Media URL is not a valid URL.
How to fix: supply a valid absolute URL.
insufficient_data
- Type:
invalid_request_error - HTTP status: 404
- Summary: Not enough audience data has been collected for this screen yet.
How to fix: retry after the screen has accumulated more impressions.
segment_not_found
- Type:
invalid_request_error - HTTP status: 404
- Summary: Audience segment not found.
How to fix: list segments first to find a valid ID.
segment_limit_exceeded
- Type:
invalid_request_error - HTTP status: 400
- Summary: Maximum number of audience segments reached.
How to fix: delete unused segments before creating a new one.
invalid_segment_rules
- Type:
invalid_request_error - HTTP status: 400
- Summary: One or more segment rules are invalid.
How to fix: every rule must have field, operator, and value.
Rate Limiting
rate_limit_exceeded
- Type:
rate_limit_error - HTTP status: 429
- Summary: Too many requests in the current window. Retry after the advertised time.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary: The caller has exhausted their daily API quota.
How to fix: wait until the next UTC day or upgrade the plan.
concurrent_request_limit
- Type:
rate_limit_error - HTTP status: 429
- Summary: Too many concurrent requests are in flight for this caller.
How to fix: reduce request concurrency.
Concurrency & Idempotency
resource_locked
- Type:
idempotency_error - HTTP status: 409
- Summary: The resource is currently being modified by another request.
How to fix: retry after the in-flight request completes.
stale_data
- Type:
idempotency_error - HTTP status: 409
- Summary: The resource has been modified since it was last fetched.
How to fix: re-fetch the resource and resend the update with the new version.
idempotency_key_in_use
- Type:
idempotency_error - HTTP status: 409
- Summary: A request with this idempotency key is already in flight.
How to fix: retry with the same key once the previous request completes, or use a fresh key.
idempotency_key_mismatch
- Type:
idempotency_error - HTTP status: 422
- Summary: The idempotency key was reused with a different request body.
How to fix: either match the original body or use a fresh idempotency key.
idempotency_error
- Type:
idempotency_error - HTTP status: 409
- Summary: Generic idempotency failure.
How to fix: consult the more specific idempotency_* codes and retry.
API Errors
internal_error
- Type:
api_error - HTTP status: 500
- Summary: An unexpected server error occurred. See
request_idfor the log correlation token.
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
- Type:
api_error - HTTP status: 500
- Summary: A database error occurred.
How to fix: retry after a backoff.
service_unavailable
- Type:
api_error - HTTP status: 503
- Summary: A downstream service is temporarily unavailable.
How to fix: retry after a short backoff.
CREATIVE_MODERATION_UNAVAILABLE
- Type:
api_error - HTTP status: 502
- Summary: The image bytes were staged but native moderation publication failed; the API allowance is released. Retry the bytes with a new operation key.
POLICY_NOT_AVAILABLE
- Type:
api_error - HTTP status: 503
- Summary: The native campaign policy is unavailable.
BRAND_CONFIGURATION_FAILED
- Type:
api_error - HTTP status: 503
- Summary: The native brand profile could not enable this funded reactive advertisement.
device_offline
- Type:
api_error - HTTP status: 503
- Summary: The device is currently offline and cannot answer the request.
How to fix: retry once the device reconnects, or send the command via a queueable endpoint.
analytics_unavailable
- Type:
api_error - HTTP status: 503
- Summary: The audience analytics service is temporarily unavailable.
How to fix: retry after a short backoff.
timeout
- Type:
api_error - HTTP status: 504
- Summary: The upstream operation exceeded its time budget.
How to fix: retry. If the problem persists, contact support with the request_id.
error
- Type:
api_error - HTTP status: 500
- Summary: Unspecified error. Callers should inspect
messageand retry if appropriate.
How to fix: fall back to retry with backoff; escalate with request_id if the issue is persistent.
device_identity_underivable
- Type:
api_error - HTTP status: 400
- Summary:
POST /v1/partner/device(V2 registration) could not derive a canonical device identity — make, model, and OS — from the supplieduastring andhardwareoverrides.
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
- Type:
permission_error - HTTP status: 403
- Summary: The requested screen is outside the API key's
scoped_screen_idsallow-list.
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
- Type:
permission_error - HTTP status: 403
- Summary: The FEIN DSL dataset does not support screen-level scoping.
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
- Type:
invalid_request_error - HTTP status: 400
- Summary: The
datasetbody field is not one of the allowed FEIN DSL datasets.
How to fix: read help.allowed on the error payload for the list of valid dataset names.
invalid_dimensions
- Type:
invalid_request_error - HTTP status: 400
- Summary: One or more requested dimensions are not defined on the target dataset.
How to fix: help.allowed lists the dimensions defined on the dataset; use only those.
invalid_measures
- Type:
invalid_request_error - HTTP status: 400
- Summary: One or more requested measures are not defined on the target dataset.
How to fix: help.allowed lists the measures defined on the dataset; use only those.
invalid_order_by
- Type:
invalid_request_error - HTTP status: 400
- Summary: The
order_byfield is not in the selected dimensions/measures.
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
- Type:
invalid_request_error - HTTP status: 400
- Summary: The request did not carry a usable client IP.
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
- Type:
authentication_error - HTTP status: 401
- Summary: The authenticated API key has no associated partner/owner.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary:
/v2/sdk/enrichexceeded 300 requests per minute for this API key.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary:
/v2/sdk/qr/scanexceeded 100 requests per minute for this API key.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary:
/v2/sdk/qr/beaconexceeded 200 requests per minute for this API key.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary:
/v2/sdk/impressionexceeded 1000 requests per minute for this API key.
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
- Type:
invalid_request_error - HTTP status: 404
- Summary: The requested device agent type has no OTA manifest entry.
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
- Type:
rate_limit_error - HTTP status: 429
- Summary:
/v2/sensing/paired-training-samplesexceeded 30 uploads per minute per device.
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
- Type:
authentication_error - HTTP status: 401
- Summary: Device identity is missing after
requireDeviceRequestAuthpassed.
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
- Type:
api_error - HTTP status: 500
- Summary: Persisting the paired-training sample batch failed server-side.
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
- Type:
authentication_error - HTTP status: 401
- Summary: The OpenRTB API key is missing or invalid.
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
- Type:
api_error - HTTP status: 500
- Summary: The server's OPENRTB_API_KEY environment variable is not configured.
How to fix: this is a server configuration issue. Contact support with the request_id from the response.
unsupported_media_type
- Type:
invalid_request_error - HTTP status: 415
- Summary: POST requests must set
Content-Type: application/json.
How to fix: add the Content-Type: application/json header to your request.
dsp_access_denied
- Type:
permission_error - HTTP status: 403
- Summary: The DSP account has been suspended or revoked.
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.