Payables

Upload invoices as payables, complete and approve them, and pay several approved payables in one batch transaction

A payable is an invoice you intend to pay. You upload the invoice, complete the payee and payment details, and the payable becomes APPROVED. You then pay several approved payables together in one batch transaction.

Prerequisites

Types and Statuses

TypePaid toPayment fields
FIATA bank accountbankAccount (bank account _id). The currency comes from the bank account.
CRYPTOA walletwallet (wallet _id) and tokenIsoCode.
StatusMeaning
DRAFTCreated. Details missing or not yet confirmed.
APPROVEDComplete and ready to pay. Still editable.
REJECTEDDeclined, with a rejectReason.
PAIDPaid. Locked.

The lifecycle:

  1. Upload or create → DRAFT.
  2. Update with the full set of fields → APPROVED. An update with only some fields moves it back to DRAFT.
  3. Add it to a batch transaction → stays APPROVED, now linked to a payment.
  4. Accept the transaction → PAID. This happens at accept, not when the payout settles.

If you delete the batch transaction, its payables return to APPROVED and are unlinked. If a payable's payment has already failed when the transaction is accepted, that payable returns to APPROVED.

Step 1: Upload the Invoice

POST /payables/upload, multipart/form-data, with one file in the field file.

RuleValue
File typesPDF, PNG, JPEG, TIFF, WebP, GIF, HEIC, HEIF
Maximum size14.25 MiB

Skyline reads the invoice and creates a DRAFT payable. The response is the payable, with:

  • name taken from the file name,
  • payableInfo holding the details read from the document (payee name, address, amount, bank or wallet details, reason),
  • amount and type filled in when the document shows them.

payableInfo is a suggestion only. It is never used to pay. If nothing could be read, the payable is still created, with empty details.

StatusMessageCause
400File type … is not allowed. Accepted: …Unsupported file type.
413File too largeOver the size limit.
400Request has unsupported document formatThe document could not be read.

Creating a Payable Without Extraction

If you already hold the payment details, upload the file with POST /file/upload (multipart, field file). It returns fileKey. Then call POST /payables:

{
  "documentS3Key": "Jane-Doe-6650aa11bb22cc33dd44ee55/invoice-1042-1727512345678",
  "name": "Invoice 1042",
  "type": "FIAT",
  "receiver": "6650b1c2d3e4f5a6b7c8d9e0",
  "bankAccount": "6650b2e3d4f5a6b7c8d9e0f1",
  "amount": "25000.00",
  "reason": "Invoice 1042"
}
FieldRequiredDescription
documentS3KeyYesThe fileKey from POST /file/upload. Without it the call fails with 500, not 400.
nameNoA label. Defaults to the file name.
typeNoFIAT or CRYPTO.
receiverNoThe receiver _id.
bankAccountFor FIATThe receiver's bank account _id.
walletFor CRYPTOThe receiver's wallet _id.
tokenIsoCodeFor CRYPTOThe token to pay in. Unknown values return 403 Token not supported.
amountNoThe amount to pay, as a decimal string. Fiat: in the bank account's currency. Crypto: in the token.
reasonNoThe payment reference. Same rules as a transaction payment reason.
bankingFeesOptionNoOUR (default), SHA or BEN.

A created payable is always DRAFT, even when every field is present. Approve it with an update.

Step 2: Complete and Approve

PATCH /payables/:id with the full set of fields for the payable's type. The server sets APPROVED only when the request body itself contains all of them. It does not merge with what is stored.

TypeFields the body must contain
FIATtype, name, amount, receiver, reason, bankAccount
CRYPTOtype, name, amount, receiver, reason, wallet, tokenIsoCode
{
  "type": "FIAT",
  "name": "Invoice 1042",
  "amount": "25000.00",
  "receiver": "6650b1c2d3e4f5a6b7c8d9e0",
  "bankAccount": "6650b2e3d4f5a6b7c8d9e0f1",
  "reason": "Invoice 1042"
}
  • Any body missing one of these fields moves the payable to DRAFT, even if it was APPROVED. Always send the full set.
  • Read status in the response to confirm APPROVED.
  • For FIAT, reason is checked against the bank account's currency. GBP: 1 to 18 characters, ^[a-zA-Z0-9\s,.\-]{1,18}$. Other currencies: 2 to 140 characters, ^[+/?:,.\s()a-zA-Z0-9]{2,140}$, no hyphens.
  • Check that amount is greater than zero. The server does not, but a batch cannot pay a zero amount.
StatusMessageCause
404No payable found.Unknown id.
403Must be the owner to update this payableThe payable belongs to another organisation.
400Already Paid payables cannot be updatedThe payable is PAID.
400Reason must contain between 2 and 140 characters…Invalid reason.
403Token not supportedUnknown tokenIsoCode.

Rejecting a Payable

POST /payables/:id/reject

{ "rejectReason": "Duplicate invoice" }

The payable moves to REJECTED. A PAID payable cannot be rejected.

Reading Payables

GET /payables/me lists your organisation's payables. It returns results, page_total and total. When a payable is in a transaction, payment.transaction shows that transaction's _id, status, tokensToSend and token.

Query parameterDescription
limitPage size. Default 20, maximum 250.
offsetNumber of results to skip. Default 0.
statusOne or more statuses, comma-separated.
typeFIAT or CRYPTO.
searchTermFree-text search over payee names, payable name, token, currency and account number.
tokens, currenciesFilter by token or currency name, comma-separated.
from, toCreation date range.
sortBycreatedAt or status. Default: newest first.
direction1 ascending or -1 descending. Applies with sortBy.

Other reads:

  • GET /payables/:id returns one payable. 404 Payable not found for an unknown id.
  • GET /payables/counts returns the number of payables per status: DRAFT, APPROVED, REJECTED, PAID.
  • GET /file?key=<documentS3Key> returns a temporary URL to download the invoice.

Step 3: Pay a Batch

Create the Batch Transaction

POST /transactions/payables

{
  "payableIds": ["6651b1c2d3e4f5a6b7c8d9e0", "6651b2c3d4e5f6a7b8c9d0e1"],
  "token": "USDC"
}

Every payable must be APPROVED, not already in a transaction, and of the same type.

StatusMessage
400Please ensure that every payable is approved state
400Please ensure that every payable is not already in a transaction
400Please ensure that every payable is from the same type

The response is a DRAFT transaction of type FIAT or CRYPTO, with one payment per payable. It has no network and no sending wallet yet.

Add the Network and Wallet

PUT /transactions/:id with the network, your organisation wallet, the token and the full payments array. Every payment must carry its payableId. The update replaces all payments. A payment sent without payableId leaves its payable linked to a deleted payment, and that payable can then never be paid or batched again.

Build each payment from its payable:

Payment fieldFiat payableCrypto payable
typeFIATCRYPTO
payableIdpayable _idpayable _id
bankAccount.idpayable bankAccount._id—
currencyIsoCodepayable bankAccount.currency.isoCode—
wallet—payable wallet._id
tokenIsoCode—payable token.isoCode
amountpayable amountpayable amount
reasonpayable reasonpayable reason
bankingFeesOptionpayable bankingFeesOption—
documentS3Keypayable documentS3Keypayable documentS3Key
{
  "token": "USDC",
  "network": "6650a1f2c3d4e5f6a7b8c9d0",
  "walletAddress": "0x4b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c",
  "payments": [
    {
      "type": "FIAT",
      "payableId": "6651b1c2d3e4f5a6b7c8d9e0",
      "bankAccount": { "id": "6650b2e3d4f5a6b7c8d9e0f1" },
      "currencyIsoCode": "EUR",
      "amount": "25000.00",
      "reason": "Invoice 1042",
      "bankingFeesOption": "OUR",
      "documentS3Key": "Jane-Doe-6650aa11bb22cc33dd44ee55/invoice-1042-1727512345678"
    },
    {
      "type": "FIAT",
      "payableId": "6651b2c3d4e5f6a7b8c9d0e1",
      "bankAccount": { "id": "6650b3f4a5b6c7d8e9f0a1b2" },
      "currencyIsoCode": "GBP",
      "amount": "4200.00",
      "reason": "INV 2231",
      "bankingFeesOption": "OUR",
      "documentS3Key": "Jane-Doe-6650aa11bb22cc33dd44ee55/invoice-2231-1727512399999"
    }
  ]
}

Validate, Submit and Accept

Continue exactly as for a single transaction:

  • Fiat payables: Crypto-to-Fiat from step 4: validate, check the allowance, approve, submit for a quote, and accept before the quote expires.
  • Crypto payables: Crypto-to-Crypto from step 3, ending with the on-chain batch payment.

The payables become PAID when the transaction is accepted, or for crypto when the on-chain batch payment is confirmed.

Endpoint Reference

EndpointMethodPurpose
/payables/uploadPOSTUpload an invoice and create a draft payable
/file/uploadPOSTUpload a file and get its key
/payablesPOSTCreate a payable from an uploaded file
/payables/:idPATCHComplete and approve a payable
/payables/:id/rejectPOSTReject a payable
/payables/meGETList payables
/payables/:idGETRead one payable
/payables/countsGETCount payables per status
/fileGETGet a download URL for the invoice
/transactions/payablesPOSTCreate a batch transaction from approved payables
/transactions/:idPUTAdd the network, wallet and payments to the batch

On this page