Blacksheep: regulated fiat ↔ crypto payments API (AU/NZ)
Blacksheep public API for builders: Ed25519-signed tRPC-style endpoints, quotes, swaps, withdrawals to bank or wallet (incl. Solana address family), webhooks, and money amount encoding.
devrels.xyz/a/200short linkBlacksheep is a regulated stablecoin / multicurrency payments product (Australia AUSTRAC + New Zealand FSP) with a public API so you can quote, swap, and withdraw between fiat and crypto programmatically — bank rails on one side, self-custody wallets on the other.
Product: blacksheep.money. Docs: docs.blacksheep.money/docs. App: my.blacksheep.money. API base: https://api.blacksheep.money. X: @Blacksheepmoney.
Related: crypto cards · Solana neobank how-to · RedotPay.
What the API is for
Docs pitch: move money between fiat and crypto programmatically — manage external bank and wallet destinations, and receive webhooks for state changes. Marketing emphasizes local rails (e.g. NPP for AUD, ACH for USD), direct bank ↔ wallet paths, and keeping a model where users can maintain custody of funds outside the platform when withdrawing to wallets.
This is a regulated payments / FX API, not a Solana DEX aggregator. Solana shows up as a wallet address family (sol) for crypto withdrawals when the asset/chain pair supports it.
Domain model
| Resource | Meaning |
|---|---|
| Account | Organisation or individual owning the API key; scopes all balances, txs, destinations, webhooks |
| Balance | Currency held under the account (accrues from deposits/swaps; spent by withdrawals/swaps) — not created directly |
| Transaction | Unit of movement (deposit, withdraw, buy/sell, swap) |
| External account | Bank account (fiat) or wallet address (crypto); created once, reused by id |
| Quote | Indicative rate before initiateSwap |
| Webhook | HTTPS URL for signed transaction.created / transaction.updated |
Transaction types and states
| Type | Direction | API |
|---|---|---|
deposit | External → Blacksheep balance | Incoming (no initiate call) |
withdraw | Balance → external account | transaction.submitWithdraw |
buy / sell | Fiat ↔ crypto | transaction.initiateSwap |
swap | Held currency A → B | transaction.initiateSwap |
| Status | Meaning |
|---|---|
PENDING | Accepted and queued |
COMPLETED | Settled |
FAILED | Could not complete |
CANCELLED | Cancelled before settlement |
Prefer webhooks over polling. No client idempotency key on mutations — a retry is a new transaction; guard duplicates on your side.
HTTP shape
| Item | Detail |
|---|---|
| Base URL | https://api.blacksheep.money |
| Method | All public endpoints POST + JSON (empty body when no input) |
| Path style | tRPC-style names, e.g. POST /v1/transaction.initiateSwap |
| Success | { "data": ... } |
| Error | { "error": { "code", "message" } } (auth failures may be flat 401) |
| Groups | account.*, quote.*, transaction.* |
Money encoding
Amounts are never floats. Wire format:
{
"value": "1500000",
"code": "USDC",
"decimals": 6,
"chain": "ethereum"
}value is a string-encoded integer in the smallest unit; crypto also carries chain. Responses may add a metadata string field. Use BigInt / decimal libs — not Number.
Authentication (Ed25519)
No bearer tokens. Dashboard (Settings → Dev space) creates an Ed25519 keypair in-browser, registers the public key, and shows once:
| Env | Role |
|---|---|
BSK_KEY_ID | Public id → Bs-Key-Id header |
BSK_SIGNING_KEY | PKCS#8 DER private key, standard base64 — local only, never sent |
| Header | Value |
|---|---|
Bs-Key-Id | Key id |
Bs-Timestamp | Unix seconds |
Bs-Nonce | 16 random bytes, standard base64 |
Bs-Signature | Base64 Ed25519 over the signing string |
Content-Digest | sha-256=:<b64>: when body present; omit if no body |
Signing string joins length-prefixed fields with : (length first prevents delimiter injection):
{len(keyId)}:{keyId}:{len(ts)}:{ts}:{len(nonce)}:{nonce}:{METHOD}:{len(path)}:{path}:{Content-Digest}Docs ship a Node.js signRequest helper. First call example: POST https://api.blacksheep.money/v1/account.balance.getMany. Use standard base64 (not base64url) for nonce, signature, and digest.
Core flows
Swap
quote.get → (show user) → transaction.initiateSwap
→ webhook transaction.updated (COMPLETED | FAILED | …)// POST /quote.get — currency_out.value "0" means "price the output"
{
"currency_in": { "value": "100000000", "code": "USDC", "decimals": 6, "chain": "ethereum" },
"currency_out": { "value": "0", "code": "AUD", "decimals": 2 }
}
// POST /transaction.initiateSwap — both sides non-zero; out from quote ask
{
"currency_in": { "value": "100000000", "code": "USDC", "decimals": 6, "chain": "ethereum" },
"currency_out": { "value": "154700", "code": "AUD", "decimals": 2 },
"external_accounts": { "from": null, "to": null }
}
// → { "data": "<transaction-uuid>" }Refresh quotes every 5–10s on a confirmation UI. Engine validates implied rate vs market then re-quotes at execution. Native crypto↔crypto direct pairs are rejected — bridge via USDC/USDT. Minimum sizes and unsupported codes return BAD_REQUEST.
Withdraw to bank or wallet
Register external accounts once; withdraw with transaction.submitWithdraw + external_account_id. Wallet list: account.external.web3.getAll. Wallet metadata.type is one of:
| Type | Address family |
|---|---|
evm | EVM |
tvm | TVM |
sol | Solana |
btc | Bitcoin |
Match wallet family to currency_out.chain. Network fees come out of the requested amount. On-chain refs appear on the transaction when available (external_reference).
Webhooks
| Type | When | Payload |
|---|---|---|
transaction.created | New tx | { object } |
transaction.updated | Status/fields change | { object, previous_attributes } |
Register HTTPS URLs in the dashboard (not via API key endpoints). Deliveries carry webhook-id, webhook-timestamp, webhook-signature (v1a,<b64>). Signature covers id.timestamp.rawBody with the webhook’s Ed25519 public key. Respond 2xx within 20s; verify, apply ~5 minute timestamp window, and dedupe on webhook-id.
Builder checklist
| Step | Action |
|---|---|
| 1 | Create account + KYC on my.blacksheep.money |
| 2 | Create API key; store BSK_KEY_ID + BSK_SIGNING_KEY securely |
| 3 | Implement signRequest (length-prefixed Ed25519 string) |
| 4 | account.balance.getMany as smoke test |
| 5 | Register webhook URL; verify signatures |
| 6 | Fund balances; run quote → initiateSwap; then withdraw guides |
| 7 | For Solana wallets: register WEB3 external account with type: sol; match chain on withdraw |
Resources
- Docs home · API reference · Authentication
- Guides: Swapping · Withdraw to wallet · Withdraw to bank · Webhooks
- Playground
- Blacksheep on DevRels
Bottom line
Blacksheep gives ANZ-regulated fiat rails plus a clean public API: Ed25519-signed POSTs, strict money objects, quote → swap → webhook, and withdrawals to banks or multi-family wallets including Solana. For builders shipping AUD/NZD ↔ stablecoin product paths, start at auth + balances, then swap and withdraw guides — treat live chain/asset support as dashboard/docs truth, not assumptions from this summary.
Keep reading
The pay.sh catalog is full of global APIs. None of them own Australia. Milypay wraps ABR, ASIC, G-NAF, ATO super, BOM weather, AusPost, and AusPayNet BSB as HTTP 402 endpoints agents can discover, pay, and call — in AUD stablecoins on Solana.
Sphere Labs sells SpherePay: one REST API for moving money between USD/EUR/BRL bank rails and USDC/USDT/EURC on Solana and other chains. The Solana-native bank API with PIX, Offloader Wallets, and Onramper virtual accounts. Integration model, code shapes, and honest caveats.
Issuing a stablecoin used to mean becoming a stablecoin company — licensing, reserve management, custody, compliance. Brale collapses that into an API. Your brand, your stablecoin, Brale's regulated infrastructure underneath. On Solana it uses Token Extensions natively: transfer hooks for compliance checks, confidential transfers for privacy. SquareFi launched MainUSD across 150 countries without writing a mint program.
Get new articles in your inbox
Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.
