Skip to content
Terminal

Errors

Software branches on error.code. error.message is English for a person reading a log, and it can change. error.request_id matches the Terminal-Request-Id header.

Every error body also has notice and terms. See Notice and terms.

The shape

A failed request looks like this. The field path uses the request's names. A header uses the header name.

Codes

quota_exceeded.limit is one of key_daily_spend, account_monthly_spend, rules, coins, keys, webhook_destinations, destinations. destinations is the account cap of 20 of your own destinations, of any kind. Channels you post alerts to have their own limit.

A 402 for the call's own block or preview charge, and a 500, are removed from the monthly call count. See Quotas.

Idempotency

A POST that writes or charges requires Idempotency-Key: 1 to 255 visible ASCII characters with no spaces (0x21 to 0x7E), such as a UUID. The same key and the same body replay the stored success, byte for byte, with the header Idempotent-Replayed: true, for 24 hours. The same key and a different body return 409 conflict with reason idempotency_key_reused. A 4xx or 5xx deletes the claim, so you can send the same key again after you fix the request. A claim still running returns reason in_progress.

cooldown_seconds on a rule is one of 900, 1800, 3600, 14400, 43200, 86400, or 604800.

Versioning

/v1 and terminal.alert.v1 change by adding: new routes, new optional request fields, new response fields, new enum values, and new error reason values. Ignore fields you do not know. Treat an enum value you do not know as other. Removing a field, renaming one, changing a type or a unit, or changing what a field means, needs /v2 and terminal.alert.v2. Both versions are served for at least 6 months.

A missing Idempotency-Key

Example. These are not live prices.

POST /v1/rules HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY
Content-Type: application/json

{
  "kind": "percent_change",
  "scope": "symbol",
  "symbol": "ETH",
  "name": "ETH moves 5% in 1 hour",
  "cooldown_seconds": 3600,
  "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" }
}

HTTP/1.1 400 Bad Request
{
  "error": {
    "code": "validation_failed",
    "message": "Check the fields listed.",
    "fields": {
      "Idempotency-Key": "Send an Idempotency-Key header with this request: 1 to 255 printable characters, such as a UUID."
    }
  }
}

A field the route does not have

Example. These are not live prices.

POST /v1/rules HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY
Content-Type: application/json
Idempotency-Key: 8c2d1f44-0e6b-4a91-b3d7-5f0a6c8e1d29

{
  "kind": "percent_change",
  "scope": "symbol",
  "symbol": "ETH",
  "name": "ETH moves 5% in 1 hour",
  "cooldown_seconds": 3600,
  "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" },
  "bonus": true
}

HTTP/1.1 400 Bad Request
{
  "error": {
    "code": "validation_failed",
    "fields": { "bonus": "Not a field of this request." }
  }
}

An unknown path

Example. These are not live prices.

GET /v1/not-a-route HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY

HTTP/1.1 404 Not Found
{
  "error": {
    "code": "not_found",
    "message": "We could not find that. Check the id and the path."
  }
}

An id that is not on the account

Example. These are not live prices.

GET /v1/alerts/2147483646 HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY

HTTP/1.1 404 Not Found
{
  "error": { "code": "not_found" }
}

A cursor that is not valid

Example. These are not live prices.

GET /v1/events?cursor=not-a-cursor&wait=0 HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY

HTTP/1.1 400 Bad Request
{
  "error": {
    "code": "validation_failed",
    "fields": {
      "cursor": "This cursor is not valid. Use next_cursor from an earlier answer, or leave it out."
    }
  }
}

Create a rule

The condition uses the public names. threshold_pct is the size, without a sign. direction is up, down, or either.

Example. These are not live prices.

POST /v1/rules HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY
Content-Type: application/json
Idempotency-Key: a1b2c3d4-e5f6-4789-a012-3456789abcde

{
  "kind": "percent_change",
  "scope": "symbol",
  "symbol": "ETH",
  "name": "ETH moves 5% in 1 hour",
  "cooldown_seconds": 3600,
  "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" }
}

HTTP/1.1 201 Created
{
  "rule": {
    "name": "ETH moves 5% in 1 hour",
    "kind": "percent_change",
    "scope": "symbol",
    "status": "active",
    "cooldown_seconds": 3600,
    "note_mode": "auto",
    "asset": { "symbol": "ETH", "name": "Ethereum" },
    "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" }
  }
}

The same Idempotency-Key on a different body

Example. These are not live prices.

POST /v1/rules HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY
Content-Type: application/json
Idempotency-Key: a1b2c3d4-e5f6-4789-a012-3456789abcde

{
  "kind": "percent_change",
  "scope": "symbol",
  "symbol": "ETH",
  "name": "ETH moves 5% in 1 hour again",
  "cooldown_seconds": 3600,
  "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" }
}

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "conflict",
    "reason": "idempotency_key_reused",
    "message": "This Idempotency-Key was used for a different request. Use a new key for a new request."
  }
}

The same condition again

A new Idempotency-Key with the same coin, kind, and condition returns the existing rule's id.

Example. These are not live prices.

POST /v1/rules HTTP/1.1
Authorization: Bearer $TERMINAL_API_KEY
Content-Type: application/json
Idempotency-Key: b2c3d4e5-f6a7-4890-b123-456789abcdef

{
  "kind": "percent_change",
  "scope": "symbol",
  "symbol": "ETH",
  "name": "ETH moves 5% in 1 hour",
  "cooldown_seconds": 3600,
  "condition": { "threshold_pct": 5, "window": "1h", "direction": "either" }
}

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "conflict",
    "reason": "duplicate_rule",
    "rule_id": "…",
    "message": "You already have this alert. Its id is in rule_id."
  }
}

A plan with no webhooks

Example. These are not live prices.

POST /v1/destinations HTTP/1.1
Authorization: Bearer $TERMINAL_FREE_KEY
Content-Type: application/json
Idempotency-Key: 1b9e4c70-2a55-4d18-8f06-3c7a91e0d4b2

{
  "kind": "webhook",
  "format": "terminal",
  "url": "https://hooks.example.com/free",
  "label": "Free webhook"
}

HTTP/1.1 402 Payment Required
{
  "error": {
    "code": "quota_exceeded",
    "message": "Webhooks for your software are part of Pro.",
    "limit": "webhook_destinations",
    "max": 0,
    "used": 0,
    "plan": "free"
  }
}

Create a free account

All developer pages

Terminal reports market events for conditions you set. It does not tell you to buy, sell, or hold, and it does not place trades.