Direct is in early access and subject to change. To request Direct access, contact hi@natural.com.
- Save the payee once as an external party.
- Add their bank account.
- Send over ACH, realtime, or wire.
Choose a rail
Create the external party
Create the payee withPOST /external-parties. kind (individual or business) and name are required; an individual’s name needs a first and last name. Include a complete address if you will send wires.
screeningStatus: clear, under_review, or rejected. A rejected party cannot have bank accounts stored or be paid.
Store the bank account
Store the payee’s US bank account withPOST /external-party-accounts. The response never returns the full numbers, only last4 and a mask.
supportedRailslists the rails the account is expected to accept (ach,realtime,wire). It is advisory; the authoritative check runs when you create the payment.validationStatestartsvalidatedorpending. Apendingaccount resolves asynchronously and firesexternal_party_account.validatedwhen it validates. ACH credits work while it ispending. Afailedaccount cannot be paid; store a new one.
epa_* for every later payment to that payee. List a payee’s saved accounts with GET /external-party-accounts?externalPartyId=epty_....
Send the payment
Every Direct create takes anIdempotency-Key. Retry a timed-out create with the same key and body; a reused key with a different body returns 409 idempotency_key_reused. The payment draws from your default wallet; pass walletId (wal_*) to spend from a specific wallet, and an agent with access to more than one wallet must pass it.
- ACH
- Realtime
- Wire
POST /ach with direction: "credit". companyEntryDescription (up to 10 characters, uppercased) appears on the payee’s bank statement. Optional addenda (up to 80 characters) carries remittance data to the receiving bank.status. An ACH payment looks like this:
A payment that breaches a limit starts at AWAITING_APPROVAL and moves on once it is approved. Common create errors:
Each rail’s full list is on its errors page: ACH, realtime, wire.
Track the payment
Read one payment with its rail’s get endpoint, for exampleGET /ach/{achId}, or list them with filters such as status and externalPartyId. Direct payments also appear in GET /transactions alongside the rest of your money movement.
CREATED and moves to PROCESSING once submitted. The terminal states differ by rail:
The fee is assessed at submission, so
fee is null until the payment reaches PROCESSING. Rather than polling, subscribe to the rail’s webhooks: ach.*, realtime.*, and wire.*. Approval holds emit approval.*. Payloads are in the event catalog.
Cancel a payment
ACH, realtime, and wire payments can be canceled only before submission, while they areCREATED or AWAITING_APPROVAL. Cancel takes a reason and an Idempotency-Key; once submission starts it returns 400 <rail>.not_cancellable.
Test in the sandbox
Sandbox Direct payments don’t reach a real bank. Drive them to a terminal state with simulations:Initiate payment for a customer
An agent connected to a customer can pay that customer’s vendors and contractors from the customer’s wallet. The flow is the same as above with one field added,customerPartyId, on every call: in data.attributes for creates and cancels, and as a query parameter for reads. For the connection model, read the Customers overview.
Ask for Direct permissions
Direct needs its own grants on the customer connection. Request them when you invite the customer.direct.create implies direct.read, and external_parties.create implies external_parties.read. direct.create can be granted only to an agent, so Direct calls for a customer authenticate with the agent’s key and name the run with X-Instance-ID.
403 ach.not_enabled (or realtime.not_enabled, wire.not_enabled).
Create the payee under the customer
Your customers’ party IDs (pty_*) are on GET /customers; see Manage access. External parties and their accounts belong to one party. Create them with customerPartyId so they belong to the customer. A payee you created for your own party can’t be paid from a customer’s wallet.
GET /external-parties?customerPartyId=pty_... and GET /external-party-accounts?customerPartyId=pty_...&externalPartyId=epty_....
Pay from the customer’s wallet
AddcustomerPartyId to the rail’s create. The payment draws from the customer’s default wallet, or the customer wallet you name in walletId, and counts against the limits on your connection. ACH is shown; POST /realtime and POST /wire take the same field.
customerParty relationship is the customer and wallet is the customer’s wallet, confirming the funds came from them. Abridged:
- The customer is the sender. The payee’s bank statement shows the customer’s legal name as the ACH company name (
submittedDescriptor.companyName), not yours. The customer needs a legal name, or a first and last name, or create returns400 party.descriptor_name_unavailable. - Approvals. A payment over your connection’s limits starts at
AWAITING_APPROVALand waits for the customer to approve it. - Access is rechecked. Natural checks the connection again before submitting a payment. If the customer revoked it or removed
direct.createin the meantime, the payment is not sent.
Track and cancel for the customer
PasscustomerPartyId as a query parameter on reads and in data.attributes on cancel. Without it, reads look in your own party and return 404 for the customer’s payments.
Webhooks
ach.*, realtime.*, and wire.* events for the customer’s payments go to your webhooks that include "connected" in sources, while your connection holds direct.read (implied by direct.create). Payloads are in the event catalog.