Create a disbursement

Moves money from one of your wallets to up to 100 destinations, with
the amounts you send. Each destination becomes a leg with its own
status, and names who is paid in one of five ways:

  • a payee: payeeId, from an earlier disbursement or
    POST /payees;
  • a person: emailAddress and/or mobileNumber, with name if
    they are new to PayKeeper and ssn if you have it;
  • a company: organizationName and the contact's emailAddress
    (with name for the contact and ein if you have them);
  • a company already on PayKeeper: organizationId;
  • a wallet: walletId, one of your own wallets (a holdback into a
    reserve wallet, say), or a payee's wallet.

People and companies are found or created exactly as POST /payees
would, and the leg's payeeId says who they resolved to. The same
person or company named in a later disbursement is the same payee. A
leg to anyone who has not yet claimed their account carries their
onboardingUrl, ready for your own email. A company new to PayKeeper
is paid into a wallet PayKeeper holds for it until it onboards; when
it does, that money and every later payment reach its own wallet.

The wallet must be able to cover the total plus PayKeeper's fee (see
Fees), or nothing is created: ids, rules and the balance are checked
before any payee is found or created, and before any money moves. The
balance that counts is what is left after disbursements you have
already made that are still settling, so two disbursements can never
spend the same money. One disbursement from a wallet is created at a
time; a second sent meanwhile gets 409 wallet_busy, and can simply be
sent again. The legs and the fee are written together or not at all,
and none of them moves until all of them exist. If PayKeeper cannot
confirm the balance with its ledger just then, or cannot finish writing
in time, the answer is a 503 and nothing is created.

If a destination cannot be resolved (a 409 name_mismatch, say),
nothing is created: every destination is checked before any payee is
found or created for another. A request that fails after that, with a
503, pays nobody, but a payee it created on the way stays, and a
retry finds the same payee again.

A leg doesn't post only if the wallet can no longer cover it, or
PayKeeper cancels or rejects it; its failureCode says which, and its
money stays in the source wallet. To pay it again, send a new
disbursement with just that leg, under new externalIds. A leg never
waits for money: if money leaves the wallet after the disbursement was
accepted (a returned deposit, say) and a leg can no longer be covered,
that leg is canceled with failureCode: INSUFFICIENT_FUNDS, so it can
never pay out of money that arrives later.

Every disbursement is sent with an Idempotency-Key. A retry with the
same key and body never pays twice: it gets this disbursement back,
with Idempotent-Replayed: true, even if the first response was lost
after the disbursement was accepted.

The response is the disbursement as accepted, usually PROCESSING.
The payout and disbursement webhooks tell you the outcome, and
GET /disbursements/{disbursementId} is the record.

StatuscodeWhen
400invalid_fieldA field is the wrong type or too long, there are more than 100 destinations, an emailAddress is not an email address, an ein or ssn is not nine digits (or an impossible SSN, such as area 000), a company was given a mobileNumber or an ssn, or statementDescriptor has characters other than capitals, digits and spaces.
400invalid_amountA destination's amount is not a positive money value with its currency.
400unsupported_fieldThe body has scheduledFor or waitForFunds. Neither is supported yet; a disbursement runs as soon as it is accepted.
400missing_destinationA destination names nobody: no payeeId, walletId, person, company or organizationId.
400missing_contactA destination has an ssn but no emailAddress or mobileNumber to say whose it is.
400missing_fielddestinations is missing or empty, a company destination has no contact emailAddress, or an ein came without an organizationName (a person has an ssn, not an ein).
400duplicate_external_idTwo destinations have the same externalId, or a destination has the disbursement's.
400ambiguous_destinationA destination gives more than one of payeeId, walletId and a payee's details, or an organizationId with other payee fields.
400external_reference_not_allowedsourceWalletId, a walletId or a payeeId is an ext: reference.
400payee_name_requiredA destination names a person new to PayKeeper without a name.
400invalid_bodyThe body is not a JSON object.
400idempotency_key_requiredThere is no Idempotency-Key header. Nothing is created.
400invalid_idempotency_keyThe Idempotency-Key is longer than 255 characters, is not printable ASCII, or was sent twice.
404wallet_not_foundsourceWalletId is not one of your wallets.
404destination_not_foundA walletId is neither one of your payees' wallets nor one of your own.
404payee_not_foundA payeeId is not one of your payees.
404organization_not_foundAn organizationId is not an organization on PayKeeper.
409external_id_already_existsYou already have a disbursement (or another object) with this externalId.
409leg_external_id_already_existsA destination's externalId is used by a leg of an earlier disbursement, or by another of your objects. Nothing was created.
409wallet_busyAnother disbursement from this wallet is being created. Send this one again in a moment.
409payee_busyAnother request is setting up the same new person right now. Nothing was created; send it again after Retry-After seconds.
409contact_mismatchA destination's email address and mobile number belong to two different people.
409ssn_mismatchA destination gives a person a different ssn than you gave before.
409name_mismatchA destination's email address or mobile number belongs to someone else: the family name or first name in its name is not theirs.
409ein_name_mismatchA destination's ein belongs to a company with a different name.
409idempotency_key_in_useA request with this Idempotency-Key is still running. Send it again after Retry-After seconds.
422insufficient_available_balanceThe wallet cannot cover the total plus PayKeeper's fee, after what earlier disbursements still owe.
422invalid_mobile_numberA destination's mobileNumber is not a valid mobile number.
422payee_not_createdPayKeeper could not create a destination's person.
422ssn_not_acceptedThis environment cannot store an SSN yet. Leave ssn out. Checked before anything is created.
422idempotency_key_reusedThis Idempotency-Key was already used for a different request. Use a new key.
503balance_unavailablePayKeeper could not confirm the wallet's balance with its ledger just now. Nothing was created; send it again.
503timed_outThe disbursement could not be written in time. Nothing was created; send it again.
503upstream_busyPayKeeper's banking provider asked it to slow down. Send it again after Retry-After seconds, with the same Idempotency-Key.
Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params
uuid
required

The wallet to pay from. PayKeeper id only.

destinations
array
required
length between 1 and 100
destinations*
uuid
required

One of your payees, by the payeeId a payee or an earlier leg gave you. Paid into the payee's wallet as it is now, so a company that has finished setting up is paid into its own wallet. PayKeeper ids only.

amount
object
required

Greater than zero.

string
length ≤ 500

The line the payee sees for this payment in PayKeeper. Defaults to the disbursement's description.

string
length ≤ 200

Your id for this leg, unique within your organization.

string
length ≤ 500
string
length ≤ 35
^[A-Z0-9 ]+$

Capitals, digits and spaces. Stored and returned; not printed on any bank statement in this version.

string
length ≤ 200

Your id for the disbursement, unique within your organization. Makes retries safe.

metadata
object

Your own string tags, returned as you sent them. At most 20 keys; keys up to 40 characters; values up to 500.

Headers
string
required
length between 1 and 255

A key you generate for this request (a UUID is best) and send again only to retry it, with the same body. Up to 255 printable ASCII characters. See Retries in the introduction.

Responses

Language
Credentials
Basic
base64
:
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json