Crypto invoicing, equivalents and payments
Space Invoices supports crypto in three independent ways:
- Crypto equivalents — show a read-only crypto amount alongside a normal fiat invoice. The document stays invoiced and settled in its fiat currency.
- Crypto invoicing — price a document’s own
currency_codedirectly in a crypto asset (for exampleBTC); no opt-in through the API. Tax and totals are still converted to the entity’s fiat currency for reporting. - Crypto payments — record a payment with
type: "crypto". It is opt-in: available once crypto invoicing or crypto equivalents is enabled for the entity, or on a document priced in crypto.
None of these require a specific wallet or blockchain integration — Space Invoices does not watch, verify, or reconcile on-chain transactions. It stores the wallet address you configure and prints it; you settle and confirm payment yourself.
Supported Assets
| Code | Name | Decimals |
|---|---|---|
BTC | Bitcoin | 8 |
ETH | Ether | 8 |
SOL | Solana | 6 |
USDC | USD Coin | 2 |
USDT | Tether | 2 |
Amounts in a crypto currency are always rounded to that asset’s decimals, not the usual 2 decimal places — a BTC-priced invoice keeps up to 8 decimal places.
1. Configure Wallets
Wallet addresses live on the entity, one per asset code, and are looked up by code wherever a wallet needs to be shown (crypto equivalents, the issuer snapshot on a crypto-priced document). You never enter a wallet per document.
await sdk.entities.update(entity.id, {
settings: {
crypto_wallets: [
{ code: "BTC", address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", network: "Bitcoin" },
{ code: "USDC", address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976", network: "Ethereum" },
],
},
});Rules:
- up to 5 wallets, one per asset code (no duplicates)
addressis 20–128 characters, letters/digits/colons onlynetworkis an optional free-text label printed next to the address (for example"Ethereum","Solana","Tron")settingskeys merge per top-level key, so this call does not touch any other settings — send onlycrypto_wallets- send
crypto_wallets: nullto clear all configured wallets
2. Crypto Equivalents on Fiat Documents
Turn on settings.crypto_equivalents to add a read-only crypto amount to invoices, advance invoices, and estimates. Credit notes and delivery notes always report crypto_equivalents: null. The document itself is unaffected — it is still created, finalized, and paid in its own fiat currency_code.
await sdk.entities.update(entity.id, {
settings: {
crypto_equivalents: { enabled: true, codes: ["BTC", "USDC"] },
},
});codestakes up to 3 unique asset codes, and needs at least one whenenabledistrue- a document denominated in crypto (see below) never gets a
crypto_equivalentsvalue — it would be redundant
Each crypto_equivalents entry looks like this on a EUR invoice with a €1,220.00 total, BTC at 74,462.51 EUR/BTC:
{ "code": "BTC", "amount": 0.01638408, "rate": 74462.51, "quote_currency": "EUR", "source": "auto", "date": "2026-01-15T00:00:00.000Z", "wallet_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "network": "Bitcoin"}amountis the document total divided byrate, rounded to the asset’s own decimals- when a configured asset’s rate cannot be resolved, that asset is silently skipped; if none resolve,
crypto_equivalentsisnull(the document is still created) wallet_address/networkare looked up fromcrypto_walletsat the time the equivalent is computed
What happens on update:
- changing
currency_codeordatere-resolves rates for the new currency/date under the entity’s current settings — ifcrypto_equivalentsis now disabled, this naturally resolves tonull - changing only the line items rescales the existing equivalents using their already-stored rate (no new rate lookup)
- any other change (a note, for example) leaves
crypto_equivalentsuntouched
3. Invoices Priced in Crypto
A document’s own currency_code can be a crypto asset code (BTC, ETH, SOL, USDC, USDT), so the document is created, issued, and its balance tracked in that asset directly. No opt-in is needed through the API: it works for every entity except UK entities. (settings.crypto_invoicing is only a web-app preference that shows crypto currencies in the document currency picker.) Create the document with a crypto currency_code:
const _invoice = await sdk.invoices.create({
currency_code: "BTC",
customer: { name: "Crypto Customer" },
items: [{ name: "Service", quantity: 1, price: 0.01, taxes: [{ rate: 22 }] }],
});For a €0.01 BTC line at 22% tax, with BTC at 74,462.51 EUR/BTC, the response looks like:
{ "currency_code": "BTC", "total": 0.01, "total_with_tax": 0.0122, "total_converted": 744.63, "total_with_tax_converted": 908.44, "exchange_rate": { "rate": 74462.51, "quote_currency": "EUR", "source": "auto" }, "taxes": [{ "base": 0.01, "amount": 0.0022, "base_converted": 744.63, "amount_converted": 163.82 }], "issuer": { "crypto_wallet": { "code": "BTC", "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "network": "Bitcoin" } }, "crypto_equivalents": null}Applies to invoices, advance invoices, estimates, credit notes, and delivery notes; POST /documents/calculate previews the same numbers before you create anything. All amounts on the document (total, item totals, total_due, payments) stay at the asset’s own precision; only the *_converted fields and exchange_rate are in the entity’s fiat currency, for reporting.
issuer.crypto_wallet is a snapshot of the wallet configured for that asset code at creation/currency-change time — change crypto_wallets later and existing documents keep the old snapshot until their currency_code changes again.
Rejected (422)
| Condition | You’ll see |
|---|---|
Entity country_code is GB | ”UK entities cannot issue documents in crypto assets; HMRC treats cryptoassets as non-money — issue in GBP” |
| No resolvable exchange rate for the asset/date | ”Exchange rate is unavailable for this crypto asset and date. A crypto-currency document must have a resolvable local-currency value.” |
Nothing is persisted when any of these fire.
4. Recording Crypto Payments
Record a payment with type: "crypto" on a document priced in crypto, or on a fiat document when the entity has enabled settings.crypto_invoicing or settings.crypto_equivalents. Otherwise the API rejects it with 422 and cause.code crypto_payment_type_not_enabled (the same applies when changing an existing payment’s type to crypto, and to payments sent inline when creating a document). A document priced in crypto always accepts it, even if the setting is later switched off. Use reference for the on-chain transaction hash; amount is in the document’s own currency (crypto amount for a crypto-priced document, fiat amount otherwise):
const _payment = await sdk.payments.create({
invoice_id: invoice.id,
type: "crypto",
amount: 0.0122,
reference: "3a1b9e0c7d5f2a8b6c4d1e9f0a7b3c5d8e2f1a4b6c9d0e3f5a7b1c8d2e4f6a9b",
});Space Invoices does not verify the transaction on-chain — recording the payment marks the document as paid the same way any other payment type does.
Compliance and Exports
A crypto-priced document (currency_code is a crypto asset) can still be reported and exported anywhere that works from its fiat-converted totals:
| Works via converted (EUR/fiat) amounts | Not available for crypto-priced documents (422) |
|---|---|
| FURS (Slovenia fiscalization) | Peppol/UBL, XRechnung, ZUGFeRD (EN 16931-based e-invoicing) |
| FINA (Croatia fiscalization) | France Chorus Pro / PDP e-reporting (also EN 16931-based) |
| e-SLOG / UJP (Slovenia) | NAV Online Számla (Hungary) |
| Slovenian VAT ledger exports (KIR/VOD) | KSeF (Poland) |
| Portuguese SAF-T | FatturaPA / SdI (Italy) |
| Spreadsheet exports and reports | |
| Minimax and QuickBooks accounting exports (booked in entity currency) |
Fiscalization of crypto payments. In Slovenia (FURS) crypto is treated like cash: an invoice paid in crypto is fiscalized and cannot skip FURS (only bank-transfer or unpaid invoices can), and changing a skipped invoice’s payment to crypto is refused until it is fiscalized, exactly as for cash. A crypto-priced invoice is submitted to FURS with its EUR-converted amounts. In Croatia (FINA) every invoice is fiscalized anyway, and a crypto payment is sent with payment method (NacinPlac) O (“other”).
Crypto equivalents on a fiat document never affect any export — the underlying document is still plain fiat.
PDFs
The PDF shows the document’s own currency_code and amounts at that currency’s precision (so a BTC invoice prints BTC amounts to 8 decimals, not rounded to 2), the crypto_equivalents block when configured, and the issuer’s crypto_wallet address/network when the document currency is crypto. "crypto" is a recognized payment type on the payments list (offered in the payment forms only under the rule above).
The PDF prints the real source next to the rate rather than the API’s internal "auto"/"manual" values: crypto rates (a crypto-denominated document’s own exchange_rate and each crypto_equivalents[].rate) are printed with source “Coinbase”; fiat foreign-currency conversion rates are printed with source “ECB” (localized to a short local abbreviation on supported document locales). A manually entered rate is printed as a localized “manual rate” instead.
Payment QR codes (Slovenian UPN, EPC/SEPA and Croatian HUB3) are never generated for crypto-priced documents.
Related API
- Entities API —
PUT /entities/{id},settings.crypto_wallets,settings.crypto_equivalents,settings.crypto_invoicing(web-app picker preference) - Invoices API —
POST /invoices,POST /documents/calculate - Payments API —
POST /payments,type: "crypto" - Invoices Guide — base invoice lifecycle