# blockwallets native REST API

> Die native REST API von blockwallets: Bearer-Keys, Rechnungen in US-Cent mit idempotenten refs, Einzahlungskonten, HMAC-SHA256-signierte Webhooks, Events und erneute Zustellung.
>
> HTML: https://blockwallets.io/docs/api?lang=de · English: https://blockwallets.io/docs/api.md?lang=en

## Native REST API

Die native API ist die eigene REST-Schnittstelle von blockwallets: Rechnungen in US-Cent, eine idempotente `ref` pro Bestellung, statische Einzahlungskonten für alle Netzwerke und mit HMAC-SHA256 signierte Webhooks. Sie erzeugt dieselben Rechnungen auf demselben gehosteten Checkout wie die [Cryptomus-kompatible API](https://blockwallets.io/docs/cryptomus?lang=de), mit denselben Gebühren und demselben Guthaben.

Sie erzeugen Ihre Keys selbst im Dashboard unter „Entwickler › Integration & Keys“: einen Live- und einen Test-Key samt Webhook-Signaturgeheimnissen. Jedes wird nur einmal angezeigt; die Liste zeigt nur das Präfix. Ist für Ihr Konto die Zwei-Faktor-Authentifizierung aktiv, verlangt das Erzeugen oder Erneuern eines Live-Keys einen Code. Noch kein Händlerkonto? [Zugang anfragen](https://blockwallets.io/?lang=de#join). Sie nutzen bereits Cryptomus? Sehen Sie sich die [Cryptomus-kompatible API](https://blockwallets.io/docs/cryptomus?lang=de) an ([so wählen Sie](https://blockwallets.io/docs?lang=de#which)).

### Keys und Anfragen

Base-URL `https://blockwallets.io`. Anfragen und Antworten sind JSON. Jede Anfrage enthält:

- `Authorization: Bearer bw_live_…` oder `Bearer bw_test_…`: Das Präfix bestimmt den Modus, ein Test-Key sieht also nur Test-Rechnungen und läuft auf Testnets

- `Content-Type: application/json` bei `POST`-Anfragen

- Bewahren Sie Keys nur auf Ihrem Server auf, nie in einer Webseite oder App; wir speichern nur ihren Hash

Fehler liefern einen HTTP-Status und `{"error": {"code": "…", "message": "…"}}`: 400 `invalid_request` (ein Feld fehlt oder ist fehlerhaft), 401 `unauthorized` (Key fehlt, ist unbekannt oder deaktiviert), 404 `not_found`, 409 `conflict` (die Anfrage widerspricht dem aktuellen Zustand der Rechnung), 422 `currency_unsupported`.

### Endpoints

| Endpoint | Was er tut |
| --- | --- |
| `GET /v1/networks` | Netzwerke, die Sie im Modus dieses Keys gerade annehmen können: Mindestbetrag, Bestätigungen, übliche Ankunftszeit und Gebührenschätzung; bei Coins mit Kurs steht der aktuelle Kurs dabei |
| `POST /v1/invoices` | Eine Rechnung erstellen (201) oder die bestehende zur selben `ref` zurückgeben (200) |
| `GET /v1/invoices?ref=…` | Eine Rechnung über Ihre `ref` abfragen |
| `GET /v1/invoices/:id` | Eine Rechnung über die id abfragen |
| `POST /v1/invoices/:id/network` | Das Netzwerk einer Rechnung selbst wählen (White-Label-Checkout): liefert `address` und `pay_amount` zur Anzeige |
| `POST /v1/invoices/:id/close` | Eine offene Rechnung schließen, bei der noch keine Überweisung gesehen wurde |
| `POST /v1/deposit-accounts` | Das Einzahlungskonto eines Kunden öffnen (201) oder abrufen (200): eine dauerhafte Adresse in jedem Netzwerk |
| `GET /v1/deposit-accounts?customer_ref=…` | Ein Einzahlungskonto über `customer_ref` abfragen |
| `GET /v1/deposit-accounts/:id` | Ein Einzahlungskonto über die id abfragen |
| `GET /v1/deposits` | Die Einzahlungen eines Kontos auflisten (`account_id` oder `customer_ref`, `limit` 1–200, Standard 50) |
| `GET /v1/deposits/:id` | Eine Einzahlung abfragen |
| `GET /v1/events` | Webhook-Zustellungen zu einer Rechnung oder Einzahlung (`invoice_id` oder `deposit_id`) |
| `POST /v1/events/:id/redeliver` | Einen Webhook erneut senden (202) |

Hängen Sie `?probe=1` an eine Abfrage von Rechnung oder Einzahlungskonto, um dabei die Chain zu prüfen (höchstens alle 10 Sekunden); andernfalls erhalten Sie den letzten Stand unseres Scanners, der jede Minute prüft.

### Rechnung erstellen POST /v1/invoices

Rufen Sie `POST /v1/invoices` auf, wenn der Kunde zur Kasse geht, und leiten Sie ihn auf `pay_url` aus der Antwort weiter.

```curl
curl -X POST https://blockwallets.io/v1/invoices \
  -H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
  -d '{"ref":"order_1042","amount":4200,"title":"Order #1042",
       "return_url":"https://shop.example/cart","success_url":"https://shop.example/thanks?order=1042",
       "meta":{"user_id":42}}'
```

| Feld | Pflicht | Beschreibung |
| --- | --- | --- |
| `ref` | Ja | Ihre Bestellnummer: 1–64 Zeichen aus Buchstaben, Ziffern und `_ . : -`. Pro Modus eindeutig; dieselbe `ref` mit denselben Feldern erneut zu senden liefert dieselbe Rechnung, mit anderen Feldern ergibt es 409 |
| `amount` | Ja | Betrag in US-Cent als Ganzzahl: `4200` = $42.00. Nach der Netzwerkwahl nicht unter dem Mindestbetrag des Netzwerks |
| `title` | Ja | Was der Kunde im Checkout sieht, bis zu 64 Zeichen |
| `return_url` | Ja | Wohin „Zurück“ im Checkout führt; muss auf einer Ihrer Website-Domains liegen |
| `success_url` | Nein | Wohin der Kunde nach der Zahlung geht; Standard ist `return_url` |
| `network` | Nein | Eine Netzwerk-id aus der [Netzwerktabelle](https://blockwallets.io/docs/api?lang=de#networks) vorauswählen; weglassen, damit der Kunde im Checkout wählt |
| `customer_ref` | Nein | Ihre Kunden-id, bis zu 64 Zeichen, wird unverändert zurückgegeben |
| `lifetime` | Nein | Lebensdauer in Sekunden, 600 – 43200, Standard 3600 |
| `amount_rule` | Nein | `"any"` = alles gutschreiben, was ankommt (Aufladungen): `paid`, sobald bestätigt. Weggelassen gilt die „Betragsregel“ unter „Einstellungen › Zahlungsregeln & Gebühren“ im Dashboard |
| `accuracy_percent` | Nein | Wie viel Unterzahlung noch als bezahlt gilt, 0 – 5 (Prozent), Standard 0 |
| `subtract_percent` | Nein | Anteil der Plattformgebühr, den der Kunde zahlt, 0 – 100 (Prozent), wird zu `pay_amount` addiert; weggelassen gilt der Dashboard-Standard |
| `payment_multiple` | Nein | Ob eine Unterzahlung an dieselbe Adresse nachgezahlt werden kann; Standard aus dem Dashboard (an, solange Sie es nicht geändert haben). `false`: Die erste zu geringe Überweisung endet als `wrong_amount` |
| `meta` | Nein | Ein beliebiges JSON-Objekt bis 2 KB, wird in Abfragen und Webhooks unverändert zurückgegeben |
| `currency` | Nein | Nur `USD` (Standard); alles andere ergibt 422 |

Jede URL muss auf einer Ihrer Website-Domains liegen (Dashboard → „Entwickler › Integration & Keys“). Die Antwort ist das unten beschriebene Rechnungsobjekt; `network`, `address` und `pay_amount` werden gefüllt, sobald ein Netzwerk gewählt ist.

### Das Rechnungsobjekt

Ids beginnen mit `inv_`. `amount` ist in US-Cent; `pay_amount` und `paid_amount` sind Coin-Mengen als Dezimal-Strings im gewählten Netzwerk.

```json
{
  "id": "inv_01J8…",
  "ref": "order_1042",
  "mode": "live",
  "status": "pending",
  "amount": 4200,
  "currency": "USD",
  "title": "Order #1042",
  "meta": { "user_id": 42 },
  "network": null,
  "address": null,
  "pay_amount": null,
  "paid_amount": null,
  "payments": [],
  "pay_url": "https://blockwallets.io/pay/inv_01J8…",
  "return_url": "https://shop.example/cart",
  "success_url": "https://shop.example/thanks?order=1042",
  "created_at": "2026-11-03T08:00:00.000Z",
  "expires_at": "2026-11-03T09:00:00.000Z",
  "paid_at": null,
  "closed_at": null
}
```

| status | Bedeutung | Was Sie tun |
| --- | --- | --- |
| `pending` | Wartet auf Zahlung | — |
| `confirming` | Eine Überweisung wurde gesehen und wird bestätigt, oder sie kam zu niedrig an und kann noch aufgestockt werden | — |
| `paid` | Bezahlt | Bestellung versenden / gutschreiben |
| `paid_over` | Bezahlt und überzahlt | Dasselbe; der Überschuss liegt in Ihrem Guthaben |
| `wrong_amount` | Mit weniger als dem vollen Betrag beendet (bei Ablauf oder bei der ersten zu geringen Überweisung, wenn `payment_multiple` false ist) | Aufladungsgeschäfte können gutschreiben, was angekommen ist (`paid_amount`; bei Coins mit Kurs zusätzlich `paid_cents`); Shops bitten den Kunden, den Rest zu zahlen |
| `closed` | Abgelaufen, ohne dass etwas eingegangen ist, oder von Ihnen geschlossen | — (später eingehendes Geld wird trotzdem gutgeschrieben und gemeldet) |

Weitere Felder: `pay_url` (der gehostete Checkout), `payments` (jede Überweisung mit `txid`, `amount`, `confirmations` und `confirmed`), `expires_at`, `paid_at`, `closed_at`; BTC, LTC, DOGE, BCH und native Coins haben außerdem `rate` (US-Dollar pro Coin, 30 Minuten fest), `rate_locked_until` und `paid_cents` (bisher eingegangen, in US-Cent).

### Netzwerke

Übergeben Sie die id als `network`. `GET /v1/networks` liefert die in Ihrem Modus gerade offenen Netzwerke; ein Netzwerk kann pausiert sein, lesen Sie die Liste also lieber aus, statt Werte fest einzutragen.

| network | Coin | Chain | Minimum |
| --- | --- | --- | --- |
| `USDT_TRC20` | USDT | TRON | $1 |
| `USDT_BEP20` | USDT | BNB Chain | $1 |
| `USDC_BEP20` | USDC | BNB Chain | $1 |
| `USDT_ERC20` | USDT | Ethereum | $5 |
| `USDC_ERC20` | USDC | Ethereum | $5 |
| `USDC_BASE` | USDC | Base | $1 |
| `USDT_POLYGON` | USDT | Polygon | $1 |
| `USDC_POLYGON` | USDC | Polygon | $1 |
| `USDT_ARBITRUM` | USDT | Arbitrum | $1 |
| `USDC_ARBITRUM` | USDC | Arbitrum | $1 |
| `USDT_OPTIMISM` | USDT | Optimism | $1 |
| `USDC_OPTIMISM` | USDC | Optimism | $1 |
| `USDT_AVALANCHE` | USDT | Avalanche | $1 |
| `USDC_AVALANCHE` | USDC | Avalanche | $1 |
| `USDT_XLAYER` | USDT | X Layer | $1 |
| `USDC_XLAYER` | USDC | X Layer | $1 |
| `USD1_BEP20` | USD1 | BNB Chain | $1 |
| `FDUSD_BEP20` | FDUSD | BNB Chain | $1 |
| `DAI_ERC20` | DAI | Ethereum | $5 |
| `DAI_ARBITRUM` | DAI | Arbitrum | $1 |
| `DAI_BASE` | DAI | Base | $1 |
| `BTC` | BTC | Bitcoin | $5 |
| `LTC` | LTC | Litecoin | $1 |
| `DOGE` | DOGE | Dogecoin | $1 |
| `BCH` | BCH | Bitcoin Cash | $1 |
| `TRX` | TRX | TRON | $1 |
| `BNB` | BNB | BNB Chain | $1 |
| `ETH` | ETH | Ethereum | $5 |
| `ETH_BASE` | ETH | Base | $1 |
| `ETH_ARBITRUM` | ETH | Arbitrum | $1 |
| `ETH_OPTIMISM` | ETH | Optimism | $1 |
| `POL` | POL | Polygon | $1 |
| `AVAX` | AVAX | Avalanche | $1 |
| `OKB` | OKB | X Layer | $1 |
| `USDT_SOL` | USDT | Solana | $1 |
| `USDC_SOL` | USDC | Solana | $1 |
| `SOL` | SOL | Solana | $1 |
| `USDT_TON` | USDT | TON | $1 |
| `TON` | TON | TON | $1 |

### Einzahlungskonten (statische Adressen) POST /v1/deposit-accounts

`POST /v1/deposit-accounts` mit `{"customer_ref": "user_42"}` gibt diesem Kunden eine dauerhafte Adresse in jedem Netzwerk (alle EVM-Chains teilen sich eine Adresse). Alles, was er sendet, wird in US-Dollar gutgeschrieben, mit einem Webhook `deposit.credited` pro Überweisung.

```curl
curl -X POST https://blockwallets.io/v1/deposit-accounts \
  -H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
  -d '{"customer_ref":"user_42"}'
```

```json
{
  "id": "dac_01J8…",
  "mode": "live",
  "customer_ref": "user_42",
  "deposit_url": "https://blockwallets.io/deposit/dac_01J8…",
  "min_amount": 100,
  "currency": "USD",
  "networks": [
    { "id": "USDT_TRC20", "chain": "tron", "token": "USDT", "address": "TXy4…8kQ2", "min_amount": 100, "confirmations": 1, … },
    { "id": "USDC_BASE", "chain": "base", "token": "USDC", "address": "0x5aE1…", "min_amount": 100, "confirmations": 20, … }
  ]
}
```

- `customer_ref`: 1–128 Zeichen aus Buchstaben, Ziffern und `_ . : @ -`; derselbe Wert liefert immer dasselbe Konto. Optional `return_url` auf Ihren Domains

- Die Antwort enthält `deposit_url` (eine gehostete Seite mit Adressen, QR-Codes und letzten Einzahlungen) und `networks` (je Netzwerk `id`, `address`, `min_amount`, `confirmations`)

- Einzahlungs-ids beginnen mit `dep_`; `amount` sind US-Cent, `amount_token` die Coin-Menge, und Coins mit Kurs tragen den verwendeten `rate`

Überweisungen unter dem Mindestbetrag (meist $1, BTC $5) werden als `below_min` erfasst und nicht automatisch gutgeschrieben; kontaktieren Sie uns, damit sie gutgeschrieben werden.

### Webhooks

Wir senden JSON per POST an die Webhook-URL im Dashboard („Entwickler › Integration & Keys“; Live und Test haben jeweils eigene URL und eigenes Secret). Der Body ist das Event:

```http
POST /your/webhook
X-BW-Event: ev_01J8…
X-BW-Delivery: 1
X-BW-Signature: t=1793693052,v1=5f0c9b…

{"id":"ev_01J8…","type":"invoice.paid","created_at":"2026-11-03T08:04:12.000Z","invoice":{"id":"inv_01J8…","ref":"order_1042","status":"paid","amount":4200,…}}
```

- `X-BW-Signature`: `t=<unix seconds>,v1=<hex>`, wobei `v1` = HMAC-SHA256(Webhook-Secret dieses Modus, `t` + `.` + roher Body)

- `X-BW-Event`: die Event-id (`ev_…`); deduplizieren Sie darüber

- `X-BW-Delivery`: die Nummer des Versuchs, beginnend bei 1

| Event | Wann |
| --- | --- |
| `invoice.paid` | Die Rechnung wurde `paid` oder `paid_over` (einschließlich Geld, das nach Ablauf ankam) |
| `invoice.wrong_amount` | Die Rechnung endete als `wrong_amount` |
| `invoice.closed` | Die Rechnung ist abgelaufen, ohne dass etwas einging, oder Sie haben sie geschlossen |
| `deposit.credited` | Eine Überweisung an ein Einzahlungskonto wurde gutgeschrieben |

Prüfen Sie gegen den rohen Body, bevor Sie ihn parsen, lehnen Sie ein `t` ab, das mehr als 300 Sekunden von Ihrer Uhr abweicht, und vergleichen Sie in konstanter Zeit. `invoice` / `deposit` im Body ist dasselbe Objekt, das eine Abfrage liefert.

```nodejs
import crypto from "node:crypto";

// rawBody: the request body exactly as received (a string), header: X-BW-Signature
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return typeof parts.v1 === "string" && parts.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
```

Eine 2xx-Antwort gilt als zugestellt; andernfalls wiederholen wir nach 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 12 Stunden, 24 Stunden und 24 Stunden (insgesamt 8 Versuche).

### Events und erneute Zustellung

`GET /v1/events?invoice_id=inv_…` (oder `deposit_id=dep_…`) listet die Webhooks einer Rechnung oder Einzahlung mit `state`, `attempts`, `last_status` und `last_error`. `POST /v1/events/ev_…/redeliver` reiht ihn sofort neu ein, zum Beispiel nachdem Sie Ihren Endpoint repariert haben. Die Seite „Entwickler › Webhook-Protokoll“ im Dashboard zeigt jeden Versuch mit Anfrage und Antwort.

### Testmodus

Mit einem `bw_test_`-Key erstellte Rechnungen laufen auf Testnets und berühren nie echtes Geld. Der gehostete Checkout zeigt eine Schaltfläche „Zahlung simulieren“ (voll / halb / überzahlt), sodass Sie Status und Webhooks sehen, ohne Testnet-Coins zu besorgen.
