BTCintel
nodeok
mempool68,781 tx
node next1.08 sat/vB
retarget40 blocks
chainmainnet

Docs

Discovery and health

The MCP endpoint, the discovery descriptor, llms.txt, the contract itself, and the probes.

Generated from openapi.yaml (Btcintel native API 0.1.0). Every hash is canonical display order; every answer carries the index and node heights; errors use one envelope — see Semantics.

GET /health

Liveness probe

Response 200

Process is up.

schema

string = "ok"

Example

curl -s https://api.btcintel.io/health | jq

GET /ready

Readiness probe

200 only when the database and node answer and the index is within 2 blocks of the node.

Response 200

Serving honest data.

schema
  • ready boolean required
  • index_height integer
  • node_height integer
  • lag_blocks integer
  • reason string

Errors

  • 503 — Database or node unreachable, or index lagging; reason explains.

Example

curl -s https://api.btcintel.io/ready | jq '{ready, index_height, node_height}'

GET /metrics

Prometheus metrics

Text format 0.0.4. Gauges btcintel_index_height, btcintel_node_height, btcintel_index_lag_blocks, btcintel_index_lag_alert, btcintel_mempool_txs, btcintel_disk_free_bytes, btcintel_disk_total_bytes; histograms btcintel_http_request_duration_seconds (by route) and btcintel_rpc_latency_seconds (by method); counter btcintel_http_requests_total (route, code).

Response 200

Metrics exposition.

Example

curl -s https://api.btcintel.io/metrics | jq

GET /v1/export/stix

STIX 2.1 bundle of one subject, one entity, one source or one campaign

Spec S6.1 FR-1…FR-4 and S1.11 FR-4, FR-11 (ADR-0004 §Export; P5, P6, P20, P38, P73). Exactly one of subject, entity, source or campaign. A subject bundle carries its observable, every annotation on it (expired with valid_until, retracted with revoked), the events that name it and, with community=true (P29, never by default), the Community notes and labels as note, opinion and identity objects. An entity bundle carries the entity, its annotations (paged), its relations and its events. A source bundle pages the source's annotations by id (since bounds updated_at; cursor from the previous page) and carries its relations and events on the first page. A campaign bundle pages the campaign's members the same way, delisted ones with valid_until, and carries the campaign's report on the first page and a campaign object on every page: a member with an abuse word is an indicator that indicates the campaign, and a victim is a note on its observable and the campaign, never an indicator. Only current claims export: a provisional member is in no bundle, and include=provisional is refused. confidence_class and source_kind narrow the claims (events follow the source kind only). Objects derived from non-redistributable sources never enter a bundle; a note lists the omitted sources and x_btcintel_omitted_sources repeats them. Ids are deterministic (type--uuid5(NS, type + ":" + key); SCO ids per the STIX rule), so bundles merge.

Parameters

NameInTypeDescription
subjectquerystring<kind>:<key>, e.g. address:bc1q…
entityquerystring—
sourcequerystring—
campaignquerystringAn event id, evt_<n>: the campaign whose members export (S1.11).
confidence_classquerystring · max length 100Only claims of this confidence class (e.g. authority_data; the confidence: prefix is optional). Levels are never combined (P6).
source_kindquerystring · max length 100Only claims from sources of this kind (sanctions, crime-intel, osint-report, …).
sincequerystringA date (2006-01-02) or an RFC 3339 timestamp; source, entity and campaign exports.
cursorquerystring—
limitqueryinteger · ≥ 1 · ≤ 500 · default 200—
communityqueryboolean · default falseSubject exports only (P29).

Response 200

The bundle; X-Next-Cursor and x_btcintel_next_cursor when a further page exists.

Headers: X-Next-Cursor

schema
  • type string required · one of "bundle"
  • id string required
  • objects array of object required
    • type string required
    • id string required
    • any other property allowed
  • x_btcintel_bundle_sha256 string required — SHA-256 of the RFC 8785 serialisation of objects.
  • x_btcintel_omitted_sources array of string
  • x_btcintel_next_cursor string

Errors

  • 400 — Malformed input (invalid_input).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.

Example

curl -s https://api.btcintel.io/v1/export/stix | jq '{type, id, x_btcintel_bundle_sha256}'

GET /v1/export/tagpack

TagPack (GraphSense YAML) of one source or one campaign

Spec S6.1 FR-5 and S1.11 FR-4, FR-11 (P73). Exactly one of source or campaign. A source's file: its current address annotations as tags, confidence the class id (in the header, and on a tag when it differs), category and abuse the DW-VA short ids, context a JSON string with the claim id, its citation, the entity, and for a campaign member its role and campaign; 403 when the source is not redistributable (P38). A campaign's file: its current address members as tags, each with its own confidence and source; victims are never tags; members of non-redistributable sources are left out and named in a comment and in X-Omitted-Sources. Only current claims export: include=provisional is refused. confidence_class and source_kind narrow the tags.

Parameters

NameInTypeDescription
sourcequerystring—
campaignquerystringAn event id, evt_<n>: the campaign whose members are tags (S1.11).
confidence_classquerystring · max length 100Only claims of this confidence class (e.g. authority_data; the confidence: prefix is optional). Levels are never combined (P6).
source_kindquerystring · max length 100Only claims from sources of this kind (sanctions, crime-intel, osint-report, …).

Response 200

The TagPack file (YAML).

Headers: X-Omitted-Sources

schema
  • title string required
  • creator string required
  • description string
  • lastmod string required — YYYY-MM-DD
  • currency string required · one of "BTC"
  • confidence string — The source's default confidence class id.
  • source string
  • tags array of object required
    • address string required
    • label string required
    • category string — The DW-VA entity short id.
    • abuse string — The DW-VA abuse short id.
    • confidence string — Present when it differs from the header.
    • lastmod string
    • source string — The fetch URL.
    • context string — JSON: claim_id, citation, entity_id, role.
    • actor string

Errors

  • 400 — Malformed input (invalid_input).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.

Example

curl -s https://api.btcintel.io/v1/export/tagpack | jq '{title, creator, description}'

GET /v1/campaigns/{claim_id}

One campaign, with its members counted by role and state

Spec S1.11 FR-3 and FR-5 (P73). The event read of /v1/claims/evt_<n> under the campaign's name: the incident with its kind, dates, source and citation, members counted by role and state (never the members themselves: list_campaign_members pages them), and its first 100 subjects with subjects_total. Any other claim id is a 400.

MCP tool
get_campaign — see For agents

Parameters

NameInTypeDescription
claim_id requiredpathstringAn event id, evt_<n>.
versionqueryinteger · ≥ 1—

Response 200

The campaign, an event claim (CC-4) with citation and content hash; ETag = content hash.

Headers: ETag

schema
  • claim_id string
  • kind string required · one of "event"
  • version integer
  • content_sha256 string
  • citation string
  • event_kind string
  • occurred_at string (date)
  • occurred_precision string · one of "day", "month", "year"
  • title string
  • summary string
  • source object
  • fetch object
  • cites array of object
  • refs array of object
  • subjects array of object — The first 100 subjects the event links (the matcher's links), in kind and key order.
  • subjects_total integer — How many subjects the event links in all.
  • entities array of object
  • members CampaignMemberCounts — The event's members (S1.11 FR-3): count is the current ones, by_role how many hold each role in each state, delisted and retracted members left out. Counts, never the list: the members page through /v1/claims/{claim_id}/members. An event with no members answers count 0 and an empty by_role.
    • count integer required
    • by_role object required
  • retracted_at string | null (date-time)
  • created_at string (date-time)

Errors

  • 400 — Malformed input (invalid_input).
  • 404 — Unknown to the index and, for transactions, to the node mempool.

Example

curl -s https://api.btcintel.io/v1/campaigns/ann_33932 | jq '{claim_id, kind, version}'

GET /v1/watch

The caller's watchlist

Spec S5.4 FR-2 (P33). The subjects a key (t2, t4) or a signed-in npub follows, with since_seq — the change-feed cursor at the time each was added — and last_seq, the feed's current head. Pull only: no webhooks (P31).

MCP tool
list_watched — see For agents
Auth
An operator credential as the bearer; every call is audited.

Response 200

The list.

schema
  • items array of object required
    • subject Subject required
      • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
      • key string — Canonical key (CC-3).
    • added_at string (date-time) required
    • since_seq integer required — The feed cursor when the subject was added.
  • count integer required
  • max integer required
  • last_seq integer required

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s https://api.btcintel.io/v1/watch \
  -H 'Authorization: Bearer $BTCI_KEY' | jq '{count, max, last_seq}'

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/watch/changes

The change feed of the watched subjects

Spec S5.4 FR-3 (P33). The S1.6 change feed after since, restricted to the caller's watched subjects, plus community: per watched subject, how many comments and labels arrived after the cursor's time (counts only, the lens applied; every event when since is 0). Poll with next_cursor; honour Retry-After on 429.

MCP tool
get_watch_changes — see For agents
Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
sincequeryinteger · ≥ 0A cursor from a previous page; 0 or absent for the start.
limitqueryinteger · ≤ 1000 · default 100—

Response 200

The page.

schema
  • items array of Change required
    • seq integer
    • at string (date-time)
    • claim_id string
    • kind string · one of "annotation", "attribution", "relation", "event"
    • action string · one of "created", "updated", "expired", "retracted"
    • subject Subject
      • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
      • key string — Canonical key (CC-3).
  • community array of object required — Per watched subject with activity after the cursor's time: counts only, never the notes.
    • subject Subject required
      • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
      • key string — Canonical key (CC-3).
    • comments integer required
    • labels integer required
  • next_cursor integer required
  • watched integer required — Subjects on the list.
  • _meta CommunityMeta required
    • notice string required — Always: Community content is indexed from Nostr and not endorsed by btcintel.
    • endorsement boolean required — Always false.

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s https://api.btcintel.io/v1/watch/changes \
  -H 'Authorization: Bearer $BTCI_KEY' | jq '{next_cursor, watched}'

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

PUT /v1/watch/{kind}/{key}

Watch a subject

Spec S5.4 FR-1, FR-2. Adds the subject (canonicalised, CC-3) to the caller's list; idempotent (200 when it was there, 201 when added); 429 when the list is full (watchlist_max: t2 10, t3 25, t4 100). since_seq is the feed's head now: poll /v1/watch/changes?since= from it.

MCP tool
watch_subject — see For agents
Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
kind requiredpathstring · one of "address", "tx", "block"—
key requiredpathstringThe address, txid or block hash.

Response 200

Already watched.

schema
  • subject Subject required
    • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
    • key string — Canonical key (CC-3).
  • added boolean required
  • since_seq integer required — The change feed's head now.
  • watched integer required
  • max integer required

Response 201

Added.

schema
  • subject Subject required
    • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
    • key string — Canonical key (CC-3).
  • added boolean required
  • since_seq integer required — The change feed's head now.
  • watched integer required
  • max integer required

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X PUT https://api.btcintel.io/v1/watch/address/bc1qz654h5qcqzalv6ltpxsk4ksdcc3e20wkedqntgnarv3a0t2e2p7scr48pl \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

DELETE /v1/watch/{kind}/{key}

Stop watching a subject

Spec S5.4 FR-2. Removes the subject from the caller's list; 404 when it was not there.

MCP tool
unwatch_subject — see For agents
Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
kind requiredpathstring · one of "address", "tx", "block"—
key requiredpathstring—

Response 200

Removed.

schema
  • removed boolean required
  • subject Subject required
    • kind string · one of "address", "script", "tx", "block", "outpoint", "pool", "descriptor", "pubkey"
    • key string — Canonical key (CC-3).

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X DELETE https://api.btcintel.io/v1/watch/address/bc1qz654h5qcqzalv6ltpxsk4ksdcc3e20wkedqntgnarv3a0t2e2p7scr48pl \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/nip98

Sign in with a NIP-98 event and open a T3 session

Spec S5.1 FR-1 (P11, P16). The request carries Authorization: Nostr <base64(kind 27235 event)> per NIP-98: tag u = this exact URL, method = POST, created_at within ±60 s, a payload tag (SHA-256 of the body) whenever a body is sent; the signature verifies; the event id is single use. A nonce tag (spec S5.8) must equal the btci_login cookie that GET /v1/auth/nonce set in this browser and name an unspent nonce; the login spends it. While LOGIN_NONCE_REQUIRED is false a login without the tag is accepted and counted; with it true, refused (401). On success a session of 30 days is created and the cookie btci_session is set (HttpOnly; Secure; SameSite=Lax; Path=/; Domain=.btcintel.io). No account, password or email exists: the identity is the public key, and no private key ever reaches btcintel. At most 10 logins per key and 30 per address each hour (FR-4).

Auth
An operator credential as the bearer; every call is audited.

Response 201

The session is open; the cookie is on the response.

Headers: Set-Cookie

schema
  • pubkey string required — 64 hex characters.
  • npub string
  • tier string required · one of "t3"
  • expires_at string (date-time) required
  • session_id string required — The row id (SHA-256 of the cookie token) as /v1/auth/me lists it.
  • quota Quota
    • requests_per_min integer
    • burst_per_min integer
    • requests_per_day integer
    • bulk_max_subjects integer
    • publish_per_day integer
    • feedback_per_day integer — Feedback entries per caller per UTC day (CC-8, spec S3.4).
    • proposals_per_day integer — Curated proposals per caller per UTC day (CC-8, spec S5.1 FR-10).
    • disputes_per_day integer — Disputes per caller per UTC day (CC-8, spec S5.3 FR-1).
    • scopes array of string

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X POST https://api.btcintel.io/v1/auth/nip98 \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/auth/nonce

A nonce that binds the next NIP-98 login to this browser

Spec S5.8 FR-1 (CP3 finding 17). 32 random bytes as URL-safe base64, ten minutes, single use, kept in memory; the same value is set as the cookie btci_login (HttpOnly; Secure; SameSite=Lax; Path=/v1/auth/; Max-Age=600). The login event carries it in a nonce tag, and POST /v1/auth/nip98 accepts the login only when the tag and the cookie agree and the nonce is unspent: an event another page obtained opens nothing. Readable from the explorer's origins only (CORS). Sixty per client address per hour.

Response 200

The nonce; the cookie is on the response.

Headers: Set-Cookie

schema
  • nonce string required
  • expires_at string (date-time) required

Errors

  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s https://api.btcintel.io/v1/auth/nonce | jq '{nonce, expires_at}'

GET /v1/auth/me

The signed-in key or account, its tier, quota and live sessions

Spec S5.1 FR-2 and S5.5 FR-9. A session cookie (a NIP-98 login or an account sign-in) or a NIP-98 header on this request; auth says which. Sessions list the caller's own live sessions (current marks this one); user_agent_hash is the SHA-256 of the User-Agent seen at login, never the string.

Auth
An operator credential as the bearer; every call is audited.

Response 200

The caller.

schema
  • pubkey string — The t3 public key, 64 hex characters; absent for an account without a linked key.
  • npub string
  • tier string required · one of "t3", "t4"
  • auth string required — How this request was authenticated. · one of "session", "nip98"
  • account object — The account of a t4 session (spec S5.5 FR-9); absent for a t3 key.
    • id integer (int64) required
    • email string — Lower case; absent once the account is anonymised.
    • display_name string
    • providers array of string — The sign-in providers bound to the account (spec S5.5 FR-13); empty when none.
    • created_at string (date-time) required
  • aal integer — The assurance level in force on this session (spec S5.5 FR-7): 2 after a passkey sign-in or within a step-up window. · one of 1, 2
  • amr array of string — The methods that produced this session: nip98, password, link, passkey, totp, recovery, google, github, apple, oidc.
  • password_reset_only boolean — This session came from a reset link and may only set a new password (spec S5.5 FR-3).
  • second_factor array of string — The account's second factors (spec S5.5 FR-9); empty when none.
  • recovery_codes_left integer
  • signing_identity SigningIdentity — The account's live Nostr key (spec S5.6 FR-6, FR-7): custodied by btcintel's signer or connected by the person's own signer. Absent from /v1/auth/me when the account has none.
    • pubkey string required — 64 hex characters.
    • npub string required
    • source string required · one of "custodied", "nip07", "nip46", "nip55"
    • linked_at string (date-time) required
    • exportable boolean required — True for a custodied key: POST /v1/auth/npub/export hands it over.
    • exported_at string (date-time) — When a custodied key was last exported; absent until then, and while the signer does not answer.
    • clients array of SignerClient — The NIP-46 apps connected to a custodied key (spec S5.6 FR-4); empty when none, absent while the signer does not answer.
      • client_pubkey string required — The app's pubkey, 64 hex characters.
      • name string required — What the account called the app when it made the link; may be empty.
      • perms array of string required — What the app may ask for: sign_event:<kind>, sign_event, nip44_encrypt, nip44_decrypt, nip04_encrypt, nip04_decrypt.
      • connected_at string (date-time) required
      • last_used_at string (date-time) — Absent until the app's first request after connecting.
  • retired_key object — A custodied key the account's own key replaced, which the signer keeps and exports for 30 days (spec S5.9 FR-2, FR-4); absent when there is none.
    • pubkey string required
    • npub string required
    • retired_at string (date-time) required
    • kept_until string (date-time) required
  • quota Quota
    • requests_per_min integer
    • burst_per_min integer
    • requests_per_day integer
    • bulk_max_subjects integer
    • publish_per_day integer
    • feedback_per_day integer — Feedback entries per caller per UTC day (CC-8, spec S3.4).
    • proposals_per_day integer — Curated proposals per caller per UTC day (CC-8, spec S5.1 FR-10).
    • disputes_per_day integer — Disputes per caller per UTC day (CC-8, spec S5.3 FR-1).
    • scopes array of string
  • sessions array of object required
    • id string
    • created_at string (date-time)
    • expires_at string (date-time)
    • amr array of string
    • last_seen_at string | null (date-time)
    • current boolean
    • user_agent_hash string

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.

Example

curl -s https://api.btcintel.io/v1/auth/me \
  -H 'Authorization: Bearer $BTCI_KEY' | jq '{pubkey, npub, tier}'

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/logout

Revoke the current session and clear the cookie

Spec S5.1 FR-2, FR-3. With a session cookie the request must also carry X-Requested-With: btcintel and an allowed Origin (CSRF), else 403.

Auth
An operator credential as the bearer; every call is audited.

Response 200

Logged out; the cookie is cleared.

schema
  • logged_out boolean

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).

Example

curl -s -X POST https://api.btcintel.io/v1/auth/logout \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

DELETE /v1/auth/sessions/{id}

Revoke one of the caller's sessions

Spec S5.1 FR-2, FR-3. Any live session of the same key, the current one included; the CSRF proof applies to cookie callers.

Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
id requiredpathstringThe session id as GET /v1/auth/me lists it (64 hex characters).

Response 200

Revoked.

schema
  • revoked string

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.

Example

curl -s -X DELETE https://api.btcintel.io/v1/auth/sessions/1 \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/auth/backup

Download the NIP-49 backup

Spec S5.1 FR-6 and P16. Two doors, no cookie: a NIP-98 signature by the same key, or ?pubkey= with the download key in X-Btcintel-Backup-Key (the second key derived from the password on the client). Downloads by key are counted before the key is compared: at most five attempts a day per npub. The blob is still encrypted; decryption happens in the browser with the password.

Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
pubkeyquerystring64 hex characters or npub1…; with the header, when no NIP-98 signature is sent.
X-Btcintel-Backup-KeyheaderstringThe download key as 64 hex characters; the header is allowed cross-origin for the explorer.

Response 200

The backup, still encrypted.

schema

KeyBackup

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s https://api.btcintel.io/v1/auth/backup \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

PUT /v1/auth/backup

Store the NIP-49 backup of the signed-in key

Spec S5.1 FR-5 (P16). The browser generated the key and encrypted it with the person's password (NIP-49, scrypt + XChaCha20-Poly1305); btcintel stores the ncryptsec1… verbatim after a shape check (bech32, 91-byte payload, version 2), never decrypts it, never logs it and cannot recover a forgotten password. auth_key is a second key derived from the password on the client (not the password): its SHA-256 lets the blob be downloaded later without a signer (P16). A first backup may be stored from a session; replacing one needs a NIP-98 signed request.

Auth
An operator credential as the bearer; every call is audited.

Request body

schema
  • ncryptsec string required — ncryptsec1… (NIP-49).
  • auth_key string required — 32 bytes as 64 hex characters, derived from the password on the client; never the password.
  • hint string — A reminder the person wrote for themselves; never the password. · max length 80

Response 200

Replaced (NIP-98).

schema
  • pubkey string required
  • npub string
  • hint string | null
  • created_at string (date-time) required
  • updated_at string (date-time) required

Response 201

Stored.

schema
  • pubkey string required
  • npub string
  • hint string | null
  • created_at string (date-time) required
  • updated_at string (date-time) required

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X PUT https://api.btcintel.io/v1/auth/backup \
  -H 'Authorization: Bearer $BTCI_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"ncryptsec":"…","auth_key":"…"}' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

DELETE /v1/auth/backup

Delete the NIP-49 backup (NIP-98 only)

Spec S5.1 FR-5. A session cookie is refused (403); the request must be signed with NIP-98.

Auth
An operator credential as the bearer; every call is audited.

Response 200

Deleted.

schema
  • deleted boolean
  • pubkey string

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 422 — The NIP-98 event was refused (nip98_invalid): wrong URL or method, stale, bad signature, wrong payload hash, or used before; the message names the reason.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X DELETE https://api.btcintel.io/v1/auth/backup \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/auth/oauth/{provider}/start

Start a sign-in with a provider

Spec S5.5 FR-13 (phase D). Sends the browser to the provider's authorization endpoint: Authorization Code with PKCE (S256), a state and a nonce per flow, kept in memory for ten minutes; the state also travels in the btci_oauth cookie of the browser that started the flow. The provider returns to /v1/auth/oauth/{provider}/callback on the public API host and nowhere else. next is a path of the explorer to land on afterwards; anything that is not a path lands on /. With an account session on the request the flow links the provider to that account instead (the addresses must match). A provider not configured answers 404 and is absent from features.sign_in; thirty starts per client address per hour, shared with the other sign-ins.

Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
provider requiredpathstring · one of "google", "github", "apple", "oidc"—
nextquerystring · max length 512Where the explorer lands afterwards: a path such as /watchlists.

Errors

  • 302 — The browser goes to the provider.
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — unavailable (spec S5.5 FR-13): the sign-in provider did not answer its discovery document; try again in a moment.

Example

curl -s https://api.btcintel.io/v1/auth/oauth/<provider>/start \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/auth/oauth/{provider}/callback

The provider's return

Spec S5.5 FR-13. Checks the state against the cookie (single use), exchanges the code with the PKCE verifier and verifies the ID token (issuer, audience, signature against the issuer's keys, nonce); GitHub, which issues none, is asked for the user and the verified addresses instead. Then: a provider identity bound to an account signs it in (a session at aal 1, amr [provider]; the cookie is set) and the browser lands on next; an unbound identity whose verified address has no account creates one and signs in; one whose verified address belongs to an account lands on /auth/provider?link=<token>&provider=<name> on the explorer, where the person confirms once (POST /v1/auth/oauth/confirm). Failures land on /auth/provider?error=<reason>&provider=<name>: state (no flow, expired, another browser), denied (the person stopped at the provider), provider (the exchange or the token failed), email_unverified (the provider does not vouch for an address), email_mismatch (a signed-in account asked for a link and the provider vouched for another address), bound (the identity is bound to another account). No token from the provider is stored.

Parameters

NameInTypeDescription
provider requiredpathstring · one of "google", "github", "apple", "oidc"—
codequerystring—
statequerystring—
errorquerystringThe provider's reason when the person stopped there.

Errors

  • 302 — The browser lands on the explorer.
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s https://api.btcintel.io/v1/auth/oauth/<provider>/callback | jq

POST /v1/auth/oauth/{provider}/callback

The provider's return as a form POST (Apple)

Spec S5.5 FR-13. Sign in with Apple returns the code with response_mode=form_post; the fields and the outcomes are those of the GET.

Parameters

NameInTypeDescription
provider requiredpathstring · one of "google", "github", "apple", "oidc"—

Errors

  • 302 — The browser lands on the explorer.
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X POST https://api.btcintel.io/v1/auth/oauth/<provider>/callback | jq

POST /v1/auth/oauth/confirm

Link a provider to the account with the same verified address

Spec S5.5 FR-13. token is the one the callback put in the explorer's URL, single use, ten minutes. Binds the provider identity to the account whose address the provider vouched for and opens a session (aal 1, amr [provider]); next in the answer is where the flow started. 404 when the token is unknown, used or expired; 409 when the account's address changed meanwhile or the identity is bound already.

Request body

schema
  • token string required — The token from the e-mail link (URL-safe base64).

Response 201

The provider is bound and the session open; the cookie is set.

schema
  • account object required
    • id integer (int64) required
    • email string
    • display_name string
    • created_at string (date-time) required
  • tier string required · one of "t4"
  • expires_at string (date-time) required
  • session_id string required — The row id (SHA-256 of the cookie token) as /v1/auth/me lists it.
  • aal integer required · one of 1, 2
  • amr array of string required
  • password_reset_only boolean
  • next string — Where the explorer lands after a provider link (POST /v1/auth/oauth/confirm): the path the flow started with.
  • signing_identity SigningIdentity — The account's Nostr key when it has one; while custodied signing is on, a sign-in gives an account without one its custodied key first (spec S5.9 FR-1).
    • pubkey string required — 64 hex characters.
    • npub string required
    • source string required · one of "custodied", "nip07", "nip46", "nip55"
    • linked_at string (date-time) required
    • exportable boolean required — True for a custodied key: POST /v1/auth/npub/export hands it over.
    • exported_at string (date-time) — When a custodied key was last exported; absent until then, and while the signer does not answer.
    • clients array of SignerClient — The NIP-46 apps connected to a custodied key (spec S5.6 FR-4); empty when none, absent while the signer does not answer.
      • client_pubkey string required — The app's pubkey, 64 hex characters.
      • name string required — What the account called the app when it made the link; may be empty.
      • perms array of string required — What the app may ask for: sign_event:<kind>, sign_event, nip44_encrypt, nip44_decrypt, nip04_encrypt, nip04_decrypt.
      • connected_at string (date-time) required
      • last_used_at string (date-time) — Absent until the app's first request after connecting.
  • quota Quota
    • requests_per_min integer
    • burst_per_min integer
    • requests_per_day integer
    • bulk_max_subjects integer
    • publish_per_day integer
    • feedback_per_day integer — Feedback entries per caller per UTC day (CC-8, spec S3.4).
    • proposals_per_day integer — Curated proposals per caller per UTC day (CC-8, spec S5.1 FR-10).
    • disputes_per_day integer — Disputes per caller per UTC day (CC-8, spec S5.3 FR-1).
    • scopes array of string

Errors

  • 400 — Malformed input (invalid_input).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X POST https://api.btcintel.io/v1/auth/oauth/confirm \
  -H 'Content-Type: application/json' \
  -d '{"token":"…"}' | jq

POST /v1/auth/npub/custodied

Create a custodied signing key for the account

Spec S5.6 FR-6 (P56), aal 1. btcintel's signer service generates a Nostr key for an account that has none and keeps it encrypted; the API stores the public key alone (access.account_npubs, source custodied). One live key per account: 409 while one is linked, custodied or own. 501 on a deployment without the signer service (SIGNER_URL). The key signs and exports only while features.signing is true; it stays encrypted meanwhile.

Auth
An operator credential as the bearer; every call is audited.

Response 201

The account's new key.

schema
  • pubkey string required — 64 hex characters.
  • npub string required
  • source string required · one of "custodied", "nip07", "nip46", "nip55"
  • linked_at string (date-time) required
  • exportable boolean required — True for a custodied key: POST /v1/auth/npub/export hands it over.
  • exported_at string (date-time) — When a custodied key was last exported; absent until then, and while the signer does not answer.
  • clients array of SignerClient — The NIP-46 apps connected to a custodied key (spec S5.6 FR-4); empty when none, absent while the signer does not answer.
    • client_pubkey string required — The app's pubkey, 64 hex characters.
    • name string required — What the account called the app when it made the link; may be empty.
    • perms array of string required — What the app may ask for: sign_event:<kind>, sign_event, nip44_encrypt, nip44_decrypt, nip04_encrypt, nip04_decrypt.
    • connected_at string (date-time) required
    • last_used_at string (date-time) — Absent until the app's first request after connecting.

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X POST https://api.btcintel.io/v1/auth/npub/custodied \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/npub/challenge

A challenge to prove an own key before linking it

Spec S5.6 FR-7, aal 1. The challenge lives five minutes and is spent by one attempt of this account. The proof for POST /v1/auth/npub/link is a kind 27235 event (NIP-98) signed by the key to link, with the tags u = url, method = POST and challenge = challenge, created within a minute of sending; the person's extension (NIP-07), remote signer (NIP-46) or Android signer (NIP-55) signs it, so no private key ever reaches the API.

Auth
An operator credential as the bearer; every call is audited.

Response 201

The challenge.

schema
  • challenge string required — 64 hex characters.
  • expires_at string (date-time) required
  • url string required — The value of the proof's u tag.
  • method string required · one of "POST"

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).

Example

curl -s -X POST https://api.btcintel.io/v1/auth/npub/challenge \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

DELETE /v1/auth/npub/{pubkey}

Unlink the account's key

Spec S5.6 FR-7 and FR-8, aal 2. An own key is unlinked at once. A custodied key must have been exported first (409 until then): unlinking destroys btcintel's encrypted copy, and the public key stays as a tombstone in the signer's audit. The signer caps erasures at thirty an hour across all accounts (CP4 round 1), then 429 quota_exceeded with Retry-After.

Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
pubkey requiredpathstring64 hex characters.

Response 200

The link ended.

schema
  • unlinked boolean required · one of true
  • pubkey string required
  • destroyed boolean required — True when the signer's copy of a custodied key was erased.

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 429 — The signer's hourly cap of erasures (quota_exceeded, Retry-After).
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X DELETE https://api.btcintel.io/v1/auth/npub/<pubkey> \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/npub/sign

Sign an event with the account's custodied key

Spec S5.6 FR-8 and FR-9, aal 1. The event comes unsigned (pubkey may be left empty; id and sig are set on the way back). It passes the same door as POST /v1/community/publish first (kinds 1111, 1985 and 5; the window; the size; NIP-73 subjects; the proof-of-work floor, mined by the client against the key's pubkey; bans), then the signer signs it within its own limits (30 a minute and 300 an hour per key). The signed event goes back to the caller, who publishes it through POST /v1/community/publish. 404 for an account without a custodied key; 409 when its key is its own; 503 signing_off while features.signing is false.

Auth
An operator credential as the bearer; every call is audited.

Request body

schema
  • event NostrEvent required — A Nostr event as NIP-01 serialises it. Signed on the way out of POST /v1/auth/npub/sign; unsigned on the way in (id and sig absent, pubkey optional).
    • id string
    • pubkey string
    • created_at integer required — Unix seconds.
    • kind integer required
    • tags array of array of string required
    • content string required
    • sig string

Response 200

The signed event.

schema
  • event NostrEvent required — A Nostr event as NIP-01 serialises it. Signed on the way out of POST /v1/auth/npub/sign; unsigned on the way in (id and sig absent, pubkey optional).
    • id string
    • pubkey string
    • created_at integer required — Unix seconds.
    • kind integer required
    • tags array of array of string required
    • content string required
    • sig string

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 422 — The event was refused at the door (event_rejected): the message carries the relay's NIP-01 prefix — unbound: (not this caller's key), invalid: (shape, subject, window, size, signature), pow: (below the floor), blocked: (kind, banned, read-only).
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X POST https://api.btcintel.io/v1/auth/npub/sign \
  -H 'Authorization: Bearer $BTCI_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"event":{"created_at":10,"kind":10,"tags":[],"content":"…"}}' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/npub/export

Export the custodied key as an nsec

Spec S5.6 FR-9 (P56), aal 2. The private key, bech32 nsec, shown once per request and never asked back; the signer records and alerts every export. Exports are capped by the signer itself (CP4-1): two a day for an account and six an hour on the server, then 429 quota_exceeded with Retry-After. The key stays custodied and keeps signing until it is unlinked. When the account's key is its own, the custodied key it replaced is exported instead while the signer keeps it (spec S5.9 FR-3, 30 days); 409 when there is none; 503 signing_off while features.signing is false.

Auth
An operator credential as the bearer; every call is audited.

Response 200

The key. Never cached, never logged; the account is told of the export by e-mail (CP4-4).

schema
  • nsec string required
  • npub string required
  • pubkey string required
  • exported_at string (date-time) required

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 429 — quota_exceeded (spec S3.1 FR-4): the caller's tier ran out of requests for the minute or the day. Retry-After says when to try again; every rate-limited answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Tier. While the deployment runs in log-only mode the decision is logged instead and X-RateLimit-Policy: log-only is set.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X POST https://api.btcintel.io/v1/auth/npub/export \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /v1/auth/npub/bunker

A bunker link to use the custodied key in another app

Spec S5.6 FR-4 (NIP-46), aal 2. A one-time link, bunker://<pubkey>?relay=wss://relay.btcintel.io&secret=…, valid ten minutes, that Primal, Amethyst, Amber, nsec.app or any NIP-46 client connects with; the explorer shows it as a QR. name is what the account calls the app; perms are what the app may ask the signer for: sign_event:<kind> per kind, sign_event for every kind, nip44_encrypt, nip44_decrypt, nip04_encrypt, nip04_decrypt; the default is signing the Community kinds (0, 1, 1111, 1985, 5). One open link per key: a new one replaces the last. 409 when the account's key is its own; 501 when the signer runs without a relay; 503 signing_off while features.signing is false.

Auth
An operator credential as the bearer; every call is audited.

Request body (optional)

schema
  • name string · max length 80
  • perms array of string

Response 201

The link; the QR payload is bunker_url.

schema
  • bunker_url string required
  • pubkey string required
  • npub string required
  • relay string required
  • perms array of string required
  • expires_at string (date-time) required

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X POST https://api.btcintel.io/v1/auth/npub/bunker \
  -H 'Authorization: Bearer $BTCI_KEY' \
  -H 'Content-Type: application/json' \
  -d '{}' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

GET /v1/auth/npub/clients

The apps connected to the custodied key

Spec S5.6 FR-4. Every NIP-46 app connected through a bunker link, with its name, permissions and last use. 404 without a custodied key; 409 when the account's key is its own.

Auth
An operator credential as the bearer; every call is audited.

Response 200

The apps; empty when none.

schema
  • clients array of SignerClient required
    • client_pubkey string required — The app's pubkey, 64 hex characters.
    • name string required — What the account called the app when it made the link; may be empty.
    • perms array of string required — What the app may ask for: sign_event:<kind>, sign_event, nip44_encrypt, nip44_decrypt, nip04_encrypt, nip04_decrypt.
    • connected_at string (date-time) required
    • last_used_at string (date-time) — Absent until the app's first request after connecting.

Errors

  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s https://api.btcintel.io/v1/auth/npub/clients \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

DELETE /v1/auth/npub/clients/{client_pubkey}

Disconnect an app from the custodied key

Spec S5.6 FR-4, aal 2. The app's next request is refused; it can only come back through a new bunker link.

Auth
An operator credential as the bearer; every call is audited.

Parameters

NameInTypeDescription
client_pubkey requiredpathstringThe app's pubkey, 64 hex characters.

Response 200

Disconnected.

schema
  • revoked boolean required · one of true
  • client_pubkey string required

Errors

  • 400 — Malformed input (invalid_input).
  • 401 — Missing or invalid bearer (unauthorized).
  • 403 — The operator lacks the role or kind the action needs, or is switched off (forbidden).
  • 404 — Unknown to the index and, for transactions, to the node mempool.
  • 409 — The state refuses the change (conflict): an open dispute by this caller exists already, or the dispute or claim is not open any more.
  • 501 — The mempool engine is not enabled on this deployment (BITCOIN_ZMQ_SEQUENCE unset).
  • 503 — signing_off (spec S5.6 FR-5): the custodied signer is switched off (SIGNING_ENABLED), keys stay encrypted and exportable later; or unavailable: the signer service is not answering.

Example

curl -s -X DELETE https://api.btcintel.io/v1/auth/npub/clients/<client_pubkey> \
  -H 'Authorization: Bearer $BTCI_KEY' | jq

An operator credential: the ops bearer or a btci_ key with the ops:agent scope.

POST /mcp

MCP Streamable HTTP endpoint (JSON-RPC 2.0)

The Model Context Protocol endpoint (spec S3.2): JSON-RPC 2.0 over Streamable HTTP as the MCP specification defines it (initialize, tools/list, tools/call, ping; GET /mcp opens the server-to-client SSE stream; DELETE /mcp ends a session). The tools are the operations of this file marked x-mcp-tool, generated by cmd/mcpgen into internal/mcp/tools.json; their inputs are the operation's parameters or body, their outputs the 200 schema rooted at type: object as MCP requires (a 200 schema that is not an object, list_sources' array, gives no output schema and a result that is the text alone), each sibling admitting the absent marker {"present": false}; the structured content is the answer when it is a JSON object, and every result carries the lag heights in _meta.btcintel. A bearer btci_ key is honoured; without one, initialize mints a t1 token and returns it in _meta.btcintel.token and in the instructions. Calls are counted in the mcp route class. Quota exhaustion is a JSON-RPC error with data {code: "quota_exceeded", retry_after}; every other API error is a tool result with isError whose text is the error envelope (with http_status), without structured content, since hosts check structured content against the tool's output schema.

Request body

schema

object — A JSON-RPC 2.0 request or notification.

Response 200

A JSON-RPC 2.0 response (application/json) or an event stream.

schema

object

Errors

  • 429 — Quota exceeded at the HTTP level (quota_exceeded, Retry-After).

Example

curl -s -X POST https://api.btcintel.io/mcp \
  -H 'Content-Type: application/json' \
  -d '{}' | jq

GET /.well-known/btcintel.json

Discovery descriptor for agents

Where everything is: the MCP endpoint and transport, the OpenAPI contract, the docs, llms.txt and the public tier ladder (spec S3.2 FR-6).

Response 200

The descriptor; cacheable for an hour.

schema
  • name string = "btcintel" required
  • version string
  • mcp object required
    • endpoint string (uri) required
    • transport string = "streamable-http" required
    • protocol_versions array of string
    • tools array of string — The enabled tool names.
  • openapi string (uri) required
  • docs string (uri) required
  • agents string (uri) — The "For agents" page.
  • llms string (uri) required
  • tiers object
    • enforcement string · one of "enforce", "log_only"
    • ladder array of object

Errors

  • 405 — Method not allowed (GET and HEAD only).

Example

curl -s https://api.btcintel.io/.well-known/btcintel.json | jq '{name, version, openapi}'

GET /.well-known/mcp/server.json

The MCP Registry entry (server.json)

This server in the MCP Registry's server.json format (schema 2025-12-11): the name io.btcintel/btcintel, the streamable-http remote, the docs page and the tools count (spec S9.4 FR-1). Served for hosts and directories that read it; the registry submission itself is the owner's step with the publisher CLI. Not an MCP tool.

Response 200

The registry entry; cacheable for an hour.

schema
  • $schema string (uri) required
  • name string = "io.btcintel/btcintel" required
  • title string
  • description string required — Carries the enabled tools count. · max length 100
  • version string required
  • websiteUrl string (uri) — The "For agents" page.
  • remotes array of object required
    • type string = "streamable-http" required
    • url string (uri) required

Errors

  • 405 — Method not allowed (GET and HEAD only).

Example

curl -s https://api.btcintel.io/.well-known/mcp/server.json | jq '{$schema, name, title}'

GET /llms.txt

llms.txt (mirror of the web's)

The llms.txt convention: title, summary, links to the docs, the OpenAPI contract, the MCP endpoint, the citation guide and the tiers.

Response 200

Plain text; cacheable for an hour.

schema

string

Errors

  • 405 — Method not allowed (GET and HEAD only).

Example

curl -s https://api.btcintel.io/llms.txt | jq