Skip to main content
These payments use the same two-step prepare → execute flow as the original V1 API: a PUT request returns EIP-712 typed data to sign, then a POST request submits the signature to broadcast the transaction.
All payment endpoints here require Authorization: Bearer <jwt> — see Authentication.
rise_id/payee/recipient/payee_riseid accept a V1 RiseID, V2 RiseID, RiseAccount address, or nanoid interchangeably — see Identifier formats.
Every amount field on this page (amount, total_amount, pay_intents[].amount) is an integer in micro-USD, V1’s original fixed-point unit — 1,000,000 = $1.00. A 100.00paymentis"amount":100000000,not100.00.Sendingadecimaldollarvaluelike100.00isparsedas100microUSD(100.00 payment is `"amount": 100000000`, not `100.00`. Sending a decimal dollar value like `100.00` is parsed as `100` micro-USD (**0.0001**) — it won’t error, it’ll just silently pay the wrong amount.

Prepare an instant payment

PUT /v1/payments/pay
wallet
string
required
Wallet address of the payer.
rise_id
string
required
V1/V2 RiseID, RiseAccount address, or nanoid of the paying team.
payee
string
required
V1/V2 RiseID, RiseAccount address, or nanoid of the payee.
amount
number | string
required
Payment amount in micro-USD (1,000,000 = $1.00) — e.g. 100000000 for $100.00.
salt
number | string
required
Unique salt for this payment. Combined with rise_id/payee/amount to derive the on-chain payment id — reusing a salt for the same payer+payee+amount collides with the existing payment and fails with 409. Use a new, never-before-used salt (e.g. a counter or random value) per payment.
domain, types, and message are the three arguments EIP-712 signing needs — see Creating the signature below. descriptor is always an empty object — a V1 response-shape placeholder with no V2 equivalent.
If this payee already received a very similar payment recently (same team, similar amount, within a lookback window), the response also includes a duplicates field — this doesn’t block the payment, it’s an early warning so you can confirm before signing:
duplicates is omitted entirely when none are found.

Execute an instant payment

POST /v1/payments/pay Same body as the prepare step, plus the signed request and signature:
request
object
required
The typed-data request object returned by the prepare step.
signature
string
required
Signature over the typed data.
title
string
Optional payment title (max 60 chars).
description
string
Optional payment description (max 1000 chars).
external_id
string
Optional caller-supplied external reference id. Not length-checked at request time — values over 500 chars are silently truncated, not rejected. (/v1/payments/batch-pay/intents is stricter: it hard-validates a 1000-char max instead.)
This is an ethers-v5-shaped transaction receipt, matching V1’s original response exactly.

Creating the signature

domain, types, and message from the prepare response feed directly into EIP-712 signing. This same pattern signs both instant and batch payments — just swap the endpoint. client.v1Legacy.pay() runs prepare → sign → execute in a single call, using the client’s configured key (or a per-call privateKey override):
For manual control over signing, use preparePay/executePay directly, mirroring the same three steps:
client.v1Legacy.batchPay() / prepareBatchPay() / executeBatchPay() follow the identical pattern for /v1/payments/batch-pay.

Manual HTTP (no SDK)

The same domain/types/messagewallet.signTypedData() → submit-as-request pattern applies to /v1/payments/batch-pay and /v1/payments/batch-pay/intents as well.

Prepare a batch payment

PUT /v1/payments/batch-pay
wallet
string
required
Wallet address of the payer.
rise_id
string
required
V1/V2 RiseID, RiseAccount address, or nanoid of the paying team.
total_amount
number | string
required
Micro-USD. Must equal the sum of all payments[].amount.
payments
array
required
Array of { recipient, amount, salt } — 1 to 1000 items. Each salt must be unique per recipient + amount (across this batch and any prior payment) — reused salts fail with 409.
Sign with wallet.signTypedData(domain, types, message) — see Creating the signature above. Unlike instant-pay, there’s no descriptor field here — but duplicates can still appear, in the same shape documented above.

Execute a batch payment

POST /v1/payments/batch-pay Same body as the prepare step, with each payment item optionally carrying title (≤60 chars), description (≤1000 chars), and external_id (silently truncated over 500 chars, not rejected), plus request and signature. Returns the same ethers-v5 receipt shape as the instant-payment execute step.

Create batch payment intents

POST /v1/payments/batch-pay/intents Creates draft (unscheduled) payments — useful for scheduling future payroll without signing immediately.
company_id
number | string
required
Legacy numeric id, V1/V2 RiseID, RiseAccount address, or nanoid of the paying company/team.
pay_intents
array
required
Array of intents, 1 to 1000 items, each: { payee_riseid, amount, salt, timestamp, title?, description?, external_id? }. amount is micro-USD and must be greater than 0 (the only payment endpoint that rejects 0, where every other one only requires non-negative). title ≤60 chars, description ≤1000 chars, external_id ≤1000 chars.
Returns 409 Conflict if a payment group or payment with the same salt already exists — use a new salt to retry.
SDK:

List payments

GET /v1/payments
company_id
number | string
required
Legacy numeric id, V1/V2 RiseID, RiseAccount address, or nanoid of the paying company/team.
external_ids
string | string[]
Filter by one or more caller-supplied external ids.
payee_ids
string | string[]
Filter by one or more payees — V1/V2 RiseID, RiseAccount address, or nanoid.
page
number
1-indexed page number (default 1).
count
number
Page size, 1–1000 (default 100). Values above 1000 are silently capped, not rejected.
status
string
all, a V2 state name (intent/ready, scheduled, complete, removed), or a V1 status (draft, pending, ready, scheduled, submitted, complete, failed, attempted_and_failed, removed, rejected, cancelled).
draft, pending, submitted, failed, attempted_and_failed, rejected, and cancelled pass validation (they’re valid V1 statuses) but have no V2 equivalent — filtering by any of them returns 200 with "data": [] rather than an error. If a filtered call unexpectedly returns nothing, check that status is one of the ones with a real V2 mapping.
SDK:

Errors

Still stuck? See Troubleshooting.