Create a new payment invoice.
Creates an invoice with a deposit address for the specified currency. Returns a checkout URL the customer can visit to pay.
Idempotency: when idempotency_key is supplied, a replay with the
same key but a different request body returns 422 with code
idempotency_key_mismatch. Replays with the same body are
idempotent and return the original invoice.
API key issued by the merchant dashboard. The key's environment (live/test) determines which data the caller can access. Permissions on the key control which endpoints are reachable.
In: header
Cryptocurrency code (e.g. BTC, ETH, SOL). Required when deferred is false. Optional when deferred is true (payer chooses at activation time).
length <= 20Blockchain network (e.g. ethereum, polygon, solana,
tron, bsc, arbitrum). Required for multi-chain tokens
whose network cannot be unambiguously inferred from the
currency ticker (USDT, USDC, ETH). Optional for
unambiguous tokens (BTC, SOL, TRX, etc.) — the backend
fills it in via currency.InferNetwork when omitted.
Sending an unsupported (currency, network) pair returns
a 400 validation_error.
length <= 30Crypto-denominated amount the merchant wants to collect, as
a decimal string in units of currency. Required unless
amount_fiat (or the legacy amount_usd) is sent instead.
Mutually exclusive with the fiat-amount fields — sending
both is a validation error.
length <= 50DEPRECATED — legacy alias for amount_fiat with
fiat_currency='USD'. Kept accepted during the Phase 4
backward-compat window (plan §C.2 rule 1 / §D). Migrate to
amount_fiat + fiat_currency.
Aliasing precedence rules (§C.2, checked in this order):
amount_usdsupplied alone (noamount_fiat, andfiat_currencyis empty or"USD") → normalized toamount_fiat+fiat_currency='USD'.- BOTH
amount_usdANDamount_fiatsupplied (fiat_currencyimplicit or explicit"USD") →amount_fiatwins,amount_usdis ignored for pricing. amount_usdsupplied together with a non-USDfiat_currency→ request is rejected with 400conflicting_amount_fieldsREGARDLESS of whetheramount_fiatis also present. Rule 3 overrides Rule 2 — the conflict check runs before the "amount_fiat wins" resolution, because silently coercing a mixedamount_usd+ non-USDfiat_currencypayload would mis-bill the merchant by the USD/ cross rate (plan §C.2 rule 3 / financial-ask #2).
length <= 50Fiat-denominated amount in the currency named by
fiat_currency (default USD). The invoice is billed
against the selected crypto at the activation-time
exchange rate. Sent instead of amount to express
"charge the customer $50 / €50 / £50 worth of BTC". When
sent without currency the invoice behaves like the
existing deferred "any crypto" flow. When sent alongside a
specific currency + network the invoice is locked to
that pair (no currency picker on checkout); this
combination requires deferred=true. Mutually exclusive
with amount.
When BOTH amount_fiat and the legacy amount_usd are
supplied, amount_fiat takes precedence (§C.2 rule 2).
However, the conflict guard in §C.2 rule 3 (amount_usd +
non-USD fiat_currency → 400 conflicting_amount_fields)
is evaluated first and overrides rule 2 — see the
amount_usd description for the full precedence list.
length <= 50ISO 4217 code that denominates amount_fiat. Defaults to
USD when omitted. Must be one of the server-side fiat
allowlist (plan §C.2 rule 7 / appsec-ask #7). Sending a
code outside the allowlist returns a 400
invalid_fiat_currency.
length <= 3"USD" | "EUR" | "GBP" | "JPY" | "AUD" | "CAD" | "CHF" | "NZD" | "SEK" | "NOK" | "DKK" | "SGD" | "HKD" | "INR" | "BRL"If true, creates a draft invoice. The payment timer starts when the payer activates via the checkout page.
falseHuman-readable invoice description.
length <= 1000Merchant-supplied external reference ID.
length <= 255Idempotency key to prevent duplicate invoices.
length <= 255Key-value metadata attached to the invoice.
properties <= 50Empty Object
URL to redirect the customer after payment.
urilength <= 2048Invoice expiration time as a minute count (0 = default).
Decimal string. Payments below this tolerance are rejected.
length <= 50Optional customer email address. When present, ss-core
dispatches an asynchronous INVOICE_PAYMENT_REQUESTED email
to this address after the invoice row is committed
(fire-and-forget goroutine; the response does not block on
Resend). The value is also copied into payer_email so the
manual /send endpoint can reuse it without re-entry.
Dispatch is idempotent against a retried CreateInvoice with
the same idempotency_key: a subsequent hit that resolves
to the existing invoice does not re-send. Phase 5 extends
the same auto-send pipeline to INVOICE_PAID and
INVOICE_EXPIRED status transitions, also guarded by
per-invoice idempotency timestamps.
Future sends are suppressed if Resend reports a hard bounce
for this address via the bounce webhook — once the row's
email_bounced flag is set, no further emails are issued
regardless of subsequent status transitions.
emaillength <= 320Response Body
const body = JSON.stringify({})fetch("https://api.halfin.xyz/api/v1/invoices", { method: "POST", headers: { "Content-Type": "application/json" }, body}){
"data": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"merchant_id": "500924a8-3f5e-4c00-beb8-2efcde988aea",
"currency": "string",
"network": "string",
"amount_requested": "string",
"amount_paid": "string",
"amount_usd": "string",
"amount_fiat": "string",
"amount_in_base_currency": "string",
"fiat_currency": "string",
"status": "draft",
"environment": "live",
"deposit_address": "string",
"deposit_address_tag": "string",
"description": "string",
"external_id": "string",
"idempotency_key": "string",
"metadata": {},
"redirect_url": "string",
"source": "string",
"static_address_id": "dfe0e820-d96d-4e58-a685-774321eca4db",
"checkout_url": "string",
"payments": [
{
"tx_hash": "string",
"amount": "string",
"confirmations": 0,
"required_confirmations": 0,
"status": "string",
"detected_at": "2019-08-24T14:15:22Z",
"settlement_conversion": {
"target_currency": "string",
"target_network": "string",
"source_amount": "string",
"target_gross_amount": "string",
"fee_amount": "string",
"net_amount": "string",
"quote_status": "not_required",
"execution_status": "not_required",
"failure_class": "string",
"quoted_at": "2019-08-24T14:15:22Z",
"locked_until": "2019-08-24T14:15:22Z"
}
}
],
"expires_at": "2019-08-24T14:15:22Z",
"paid_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z",
"is_deferred": true,
"draft_expires_at": "2019-08-24T14:15:22Z",
"activated_at": "2019-08-24T14:15:22Z",
"activation_count": 0,
"is_locked_fiat": true,
"target_settlement_asset": "passthrough",
"settlement_target_currency": "USDC",
"settlement_target_network": "ethereum",
"payer_email": "string",
"sent_at": "2019-08-24T14:15:22Z"
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}{
"error": {
"code": "string",
"message": "string",
"details": [
"string"
]
},
"meta": {
"request_id": "string"
}
}