Skip to content
Back to Bitrovas L402 Services

Card snapshot

api.bitrovas.ch · 2026-10-17 14:22:33 UTC · d5ccd90b07861b18b1ef9dfa1d9efec4326d3b2b5ab4b2443550339c6e8bb797

This is a frozen copy of the agent's agent-card.json as we observed it at the timestamp above. We capture a new snapshot every time the card's content hash changes. Useful for: forensic drift analysis, verifying downstream callers see the right version, reproducing routing decisions made historically.

{
  "name": "Bitrovas L402 Services",
  "description": "Pay-per-request Bitcoin and data utility services over HTTP with L402 (Lightning). No account, no API key; inputs are processed in memory and not stored.",
  "supportedInterfaces": [
    {
      "url": "https://api.bitrovas.ch/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "provider": {
    "organization": "Bitrovas",
    "url": "https://bitcoin.bitrovas.ch"
  },
  "version": "1.0.0",
  "documentationUrl": "https://api.bitrovas.ch/docs",
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "extensions": [
      {
        "uri": "https://a2a-registry.org/extensions/registry/v1",
        "description": "L402 payment. A2A: SendMessage returns a task in TASK_STATE_INPUT_REQUIRED with a DataPart {l402: {macaroon, token, invoice, amountSats, paymentHash}}; after paying, SendMessage with the same taskId and {l402: {token, preimage}} completes the task. REST: each skill endpoint answers HTTP 402 with a Lightning (BOLT11) invoice and a macaroon in WWW-Authenticate; repeat the request with 'Authorization: L402 <macaroon>:<preimage>'. Machine-readable price list: https://api.bitrovas.ch/.well-known/l402-services",
        "required": false,
        "params": {
          "payment": {
            "model": "paid",
            "protocols": [
              "lightning-invoice"
            ],
            "direction": "inbound",
            "rails": [
              {
                "network": "lightning",
                "token": "BTC",
                "type": "crypto",
                "protocol": "lightning-invoice",
                "settlementTime": "instant"
              }
            ],
            "skills": [
              {
                "skillId": "psbtcheck",
                "price": "21",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "poisoncheck",
                "price": "21",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "jsonrepair",
                "price": "10",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "walletaddr",
                "price": "21",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "qrbill",
                "price": "10",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "formatcheck",
                "price": "11",
                "currency": "sats",
                "unit": "request"
              },
              {
                "skillId": "timestamp",
                "price": "21",
                "currency": "sats",
                "unit": "request"
              }
            ],
            "meta": {
              "scheme": "L402",
              "unit": "sats",
              "directory": "https://api.bitrovas.ch/.well-known/l402-services",
              "auth_header": "Authorization: L402 <macaroon>:<preimage>",
              "refund_policy": "invalid input is rejected (REST HTTP 400, A2A TASK_STATE_REJECTED) before an invoice is created",
              "formatcheck_pricing": "10 sats + 1 sat per started KB (1024 bytes) of data, max 1 MiB; free quote POST https://api.bitrovas.ch/v1/formatcheck/quote"
            }
          }
        }
      }
    ]
  },
  "defaultInputModes": [
    "application/json"
  ],
  "defaultOutputModes": [
    "application/json"
  ],
  "skills": [
    {
      "id": "psbtcheck",
      "name": "Bitrovas PSBT-Check",
      "description": "Pre-sign check of an unsigned Bitcoin PSBT: recipients, amounts, change, fee/sat-vB, RBF, sighash, missing UTXO info, dust, address-poisoning lookalikes. Verdict ok|warn|danger, JSON + text DE/EN. Never signs or broadcasts. Endpoint: POST https://api.bitrovas.ch/v1/psbtcheck (also POST https://api.bitrovas.ch/v1/psbtcheck/task with {\"task\": \"<free text>\"}). Price: 21 sats per request, paid via L402 (Lightning). Input (application/json): psbt: unsigned PSBT v0, base64 or hex (required); expected_address: optional; expected_amount_sats: optional integer; network: optional mainnet|testnet|signet|regtest. Output (application/json): service, verdict ok|warn|danger, result (recipients, amounts, change, fee, findings), text_de, text_en. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"psbtcheck\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "psbtcheck"
      ],
      "examples": [
        "{\"skill\":\"psbtcheck\",\"input\":{\"psbt\":\"cHNidP8B...\",\"expected_address\":\"bc1q...\",\"expected_amount_sats\":50000}}",
        "REST: POST https://api.bitrovas.ch/v1/psbtcheck with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "poisoncheck",
      "name": "Bitrovas PoisonCheck",
      "description": "Address-poisoning check: compares the address you are about to send to (used) with the address you know (expected) and detects lookalike/vanity patterns for Bitcoin, EVM and Tron. Level poison|review|clear, JSON + text DE/EN. Single 21 sats; batch up to 50 addresses 21 + 5 sats per address. Pattern, not proof; not a send approval. Endpoint: POST https://api.bitrovas.ch/v1/poisoncheck (also POST https://api.bitrovas.ch/v1/poisoncheck/task with {\"task\": \"<free text>\"}). Price: 21 sats per request, paid via L402 (Lightning). Input (application/json): used: address to check (string) \u2013 or list of up to 50 addresses for batch; expected: address you know is correct (string; for batch also a list of the same length); pricing: {'single': 21, 'batch': '21 + 5 per address (max 50)'}. Output (application/json): verdict ok|warn|danger per address, similarity findings vs. expected/known addresses, text DE/EN. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"poisoncheck\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "poisoncheck"
      ],
      "examples": [
        "{\"skill\":\"poisoncheck\",\"input\":{\"used\":\"bc1q...\",\"expected\":\"bc1q...\"}}",
        "REST: POST https://api.bitrovas.ch/v1/poisoncheck with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "jsonrepair",
      "name": "Bitrovas JSON Validate & Repair",
      "description": "Validate and repair a JSON array of flat records (max 1000 rows): syntax repair (trailing commas, code fences, Python literals), whitespace trim, NULL normalization, duplicate removal, optional type standardization, e-mail check and JSON Schema (2020-12) validation with flag|drop. Returns cleaned JSON + machine-readable report. Deterministic; no data stored. Endpoint: POST https://api.bitrovas.ch/v1/jsonrepair (also POST https://api.bitrovas.ch/v1/jsonrepair/task with {\"task\": \"<free text>\"}). Price: 10 sats per request, paid via L402 (Lightning). Input (application/json): data: JSON array of flat objects, or {\"records\": [...]}, or a JSON string to repair (required, max 1000 rows); json_schema: optional JSON Schema 2020-12 applied per row; json_schema_action: optional flag|drop (default flag); options: optional DataCleanBot options, e.g. {\"operations\": [...], \"schema\": {col: type}, \"dedupe_keys\": [...]}. Output (application/json): repaired/cleaned JSON data plus a list of applied fixes. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"jsonrepair\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "jsonrepair"
      ],
      "examples": [
        "{\"skill\":\"jsonrepair\",\"input\":{\"data\":\"[{'a': 1,},]\"}}",
        "REST: POST https://api.bitrovas.ch/v1/jsonrepair with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "walletaddr",
      "name": "Bitrovas Wallet-Adressen",
      "description": "Watch-only address derivation from xpub/ypub/zpub/tpub or an output descriptor: receive + change addresses (configurable gap, max 1000) and ownership check of given addresses with derivation path. Offline, nothing stored. Private keys/seeds are rejected. Endpoint: POST https://api.bitrovas.ch/v1/walletaddr (also POST https://api.bitrovas.ch/v1/walletaddr/task with {\"task\": \"<free text>\"}). Price: 21 sats per request, paid via L402 (Lightning). Input (application/json): xpub: xpub/ypub/zpub/tpub/upub/vpub (account level) \u2013 or \u2013; descriptor: output descriptor with * (e.g. wpkh([fp/84h/0h/0h]xpub/<0;1>/*)); addresses: optional list of addresses to check (max 50); script: optional override for xpub/tpub: p2pkh|p2wpkh|p2sh-p2wpkh|p2tr; gap: optional, addresses per chain (default 20, max 1000). Output (application/json): derived addresses (index, address, path), optional match result for a searched address. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"walletaddr\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "walletaddr"
      ],
      "examples": [
        "{\"skill\":\"walletaddr\",\"input\":{\"xpub\":\"zpub...\",\"addresses\":[\"bc1q...\"]}}",
        "REST: POST https://api.bitrovas.ch/v1/walletaddr with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "qrbill",
      "name": "Bitrovas QR-Rechnung-Check",
      "description": "Technical offline check of a Swiss QR-bill (SPC payload) or IBAN + reference: format, IBAN checksum, CH/LI, QR-IBAN vs reference type (QRR/SCOR/NON), QR-reference mod10, ISO 11649, amount/currency, mandatory fields, lengths, character set. Optional: compares the bill's IBAN with expected supplier IBAN(s) supplied by the customer (plain or SHA-256); mismatch = danger. Only checks structure and this comparison, not whether a bill is genuine. Verdict ok|warn|danger, DE/EN + JSON. Nothing stored or logged. Endpoint: POST https://api.bitrovas.ch/v1/qrbill (also POST https://api.bitrovas.ch/v1/qrbill/task with {\"task\": \"<free text>\"}). Price: 10 sats per request, paid via L402 (Lightning). Input (application/json): payload: QR-bill SPC payload text (all lines) \u2013 or \u2013; iban: IBAN; reference: optional QRR/SCOR reference; amount: optional; currency: optional CHF|EUR; expected_iban: optional: expected supplier IBAN or list (max 20); normalized (no spaces, uppercase); expected_iban_sha256: optional: hex SHA-256 of normalized IBAN (no spaces, uppercase, ASCII) or list. Output (application/json): verdict ok|warn|danger, list of technical findings, IBAN comparison result if expected IBANs were supplied, text DE/EN. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"qrbill\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "qrbill"
      ],
      "examples": [
        "{\"skill\":\"qrbill\",\"input\":{\"iban\":\"CH4431999123000889012\",\"expected_iban\":\"CH4431999123000889012\"}}",
        "REST: POST https://api.bitrovas.ch/v1/qrbill with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "formatcheck",
      "name": "Bitrovas FormatCheck",
      "description": "Validate CSV, YAML, XML, TOML or JSON (data max 1 MiB, 1M elements). Price: 10 sats + 1 sat per started KB of data, free quote at POST /v1/formatcheck/quote. Converts to JSON (or flat JSON -> CSV); optional JSON Schema 2020-12 validation (local $ref only). Safe parsers (defusedxml, yaml.safe_load, tomllib), size and time limits, in-memory only. Invalid input = free 400 with error position. Nothing stored or logged. Endpoint: POST https://api.bitrovas.ch/v1/formatcheck (also POST https://api.bitrovas.ch/v1/formatcheck/task with {\"task\": \"<free text>\"}). Price: from 11 sats (10 + 1 per started KB), paid via L402 (Lightning). Input (application/json): data: input text (required, UTF-8, max 1 MiB); format: csv|yaml|xml|toml|json (required); action: optional validate (default) | to_json | to_csv (to_csv: format json, flat records); json_schema: optional JSON Schema 2020-12 for the parsed document (local $ref only, max 8 KB). Output (application/json): valid true|false, errors (line/path, message), converted output (JSON or CSV), optional JSON-Schema errors. A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"formatcheck\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart). Max 1 MiB data; free price quote: POST https://api.bitrovas.ch/v1/formatcheck/quote.",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "formatcheck"
      ],
      "examples": [
        "{\"skill\":\"formatcheck\",\"input\":{\"format\":\"yaml\",\"data\":\"a: 1\",\"action\":\"to_json\"}}",
        "REST: POST https://api.bitrovas.ch/v1/formatcheck with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    },
    {
      "id": "timestamp",
      "name": "Bitrovas Zeitstempel (OpenTimestamps)",
      "description": "Bitcoin timestamp for a SHA-256 hash or Nostr event id via OpenTimestamps. Returns a pending .ots proof (base64) immediately; the free GET /v1/timestamp/<id> returns the upgraded proof and block height after confirmation (usually 1\u201312 h). Only the hash is sent, never the file. Endpoint: POST https://api.bitrovas.ch/v1/timestamp (also POST https://api.bitrovas.ch/v1/timestamp/task with {\"task\": \"<free text>\"}). Price: 21 sats per request, paid via L402 (Lightning). Input (application/json): hash: SHA-256 hex (64) of your file \u2013 or \u2013; event_id: Nostr event id (hex, note1\u2026, nevent1\u2026); status: GET /v1/timestamp/<id> (free). Output (application/json): id, pending OpenTimestamps proof (.ots, base64), status_url (free GET /v1/timestamp/<id> for the upgraded proof). A2A (JSON-RPC, https://api.bitrovas.ch/a2a): SendMessage with a DataPart {\"skill\": \"timestamp\", \"input\": {...same fields as REST...}}; the task answers TASK_STATE_INPUT_REQUIRED with a DataPart {\"l402\": {macaroon, token, invoice, amountSats, paymentHash}}; pay, then SendMessage with the same taskId and a DataPart {\"l402\": {\"token\": ..., \"preimage\": ...}} to get TASK_STATE_COMPLETED with the result as artifact (DataPart).",
      "tags": [
        "bitrovas",
        "l402",
        "lightning",
        "timestamp"
      ],
      "examples": [
        "{\"skill\":\"timestamp\",\"input\":{\"hash\":\"<sha256 hex, 64 chars>\"}}",
        "REST: POST https://api.bitrovas.ch/v1/timestamp with a JSON body; answer the 402 invoice, then repeat with Authorization: L402 <token>:<preimage>"
      ],
      "inputModes": [
        "application/json"
      ],
      "outputModes": [
        "application/json"
      ]
    }
  ]
}