Transactions

The shared shape of every transaction, its statuses, the preconditions to accept, the money fields in the response, and the common errors

A transaction moves value out of or into your organisation. There are three types:

Typetype valueGuide
Crypto-to-fiat (off-ramp)FIATCrypto-to-Fiat
Crypto-to-cryptoCRYPTOCrypto-to-Crypto
Fiat-to-crypto (on-ramp)ONRAMPOn-Ramp

This page covers what the three have in common. Read it first, then follow the guide for your type.

The Common Shape

  1. Create the transaction with POST /transactions. It starts in DRAFT.
  2. Update it if needed with PUT /transactions/:id.
  3. Validate it with POST /transactions/:id/validate (crypto-to-fiat and crypto-to-crypto).
  4. Submit it with POST /transactions/:id/submit. For crypto-to-fiat this fetches a live quote. The transaction moves to WAITING_ACCEPTANCE.
  5. Accept it with POST /transactions/:id/accept. For crypto-to-fiat you must accept before the quote expires. Crypto-to-crypto has no accept call: your wallet executes the payment on-chain instead.
  6. Poll GET /transactions/:id until the transaction reaches a final status.

A transaction holds up to 30 payments. Each payment is one payout to one destination.

Before You Start

These conditions apply to the submit and accept calls:

  • Confirmed account. Otherwise: 403 Account is not confirmed.
  • Active organisation. The organisation's operationalStatus must be ACTIVE. Otherwise accept returns 401 Organisation operational status does not permit this action. Read the status from GET /users/me, under selectedOrganisation.
  • Authorised role. The API user must have the authorized member role. A viewer gets 401 Your user does not have enough permission to perform this action on accept and on the allowance check.
  • Two-factor authentication off for the integration user. With 2FA on, calls can fail with 401 2FA session expired. Please login again. Use a dedicated integration user with 2FA turned off.
  • The sending wallet is an organisation wallet. For crypto-to-fiat and crypto-to-crypto, the walletAddress you send from must be one of your organisation's own wallets, added with POST /organisations/:organisationId/add-wallet. A beneficiary wallet does not work. See Wallets.
  • Receivers and destinations exist. Register receivers, bank accounts and beneficiary wallets first. See Setup.

Updating a Transaction

PUT /transactions/:id takes the same body as create. It is allowed while the transaction is DRAFT, WAITING_ACCEPTANCE or FAILED. Otherwise it returns 400 Transaction is not in DRAFT,FAILED,WAITING_ACCEPTANCE status.

Two rules to know:

  • The payments array is replaced wholesale. The server deletes every existing payment and creates the ones you send. Always send the full list, not only the payment you changed.
  • An update resets the transaction to DRAFT. A quoted transaction loses its quote. Validate and submit again.

DELETE /transactions/:id removes a transaction in DRAFT or WAITING_ACCEPTANCE.

Statuses

The transaction status field takes these values:

StatusMeaningFinal?
DRAFTCreated or edited. Not yet submitted.No
INTERNAL_DRAFTUsed internally by Skyline. Never returned in your transaction list.No
WAITING_ACCEPTANCESubmitted. Crypto-to-fiat: quoted and waiting for your accept. Crypto-to-crypto: waiting for your on-chain payment. On-ramp: waiting for you to confirm the bank transfer.No
FAILEDSomething went wrong: an invalid payment, an expired quote or an accept error. statusInfo holds the reason.No. Fix the cause and submit again.
PROCESSINGAccepted. Skyline is executing the transaction.No
RELEASEDThe payout has been released and is on its way.No
COMPLETEDSettled.Yes
REJECTED_BY_SKYLINERejected by Skyline's review.Yes

Only COMPLETED and REJECTED_BY_SKYLINE are final. Transitions are not strictly linear: PROCESSING can go straight to COMPLETED, and RELEASED can return to PROCESSING or move to FAILED.

Each entry in payments has its own status: DRAFT, WAITING_ACCEPTANCE, PROCESSING_PAYMENT, RELEASED, FAILED, COMPLETED or REJECTED. In a transaction with several payments, one payment can fail while the others complete. Check each payment's status and statusInfo.

Tracking a Transaction

The API does not send webhooks. Poll GET /transactions/:id and read status.

  • Poll every 30 to 60 seconds. The detail response can be up to 30 seconds old, so faster polling gains nothing.
  • Stop at COMPLETED or REJECTED_BY_SKYLINE.
  • Treat FAILED as a signal to act, not as the end.

GET /transactions/:id returns the full transaction with its payments embedded and network populated. There is no separate payments endpoint. It returns 403 for a transaction that belongs to another organisation.

Listing Transactions

GET /transactions returns results, page_total and total for your organisation.

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

Money Fields in the Response

All amounts are decimal strings. Use them exactly as returned. Do not round them.

FieldMeaning
tokensToSendThe total debited from your wallet, in token units. It includes the commission and the banking fee.
totalCommissionSkyline's commission. For crypto-to-fiat it is in the token you pay with.
totalBankingFeeThe banking fee for the fiat payout. For crypto-to-fiat it is in the token you pay with.
payments[].amountWhat the payment delivers. Crypto-to-fiat: the fiat amount the receiver gets, in the bank account's currency. Crypto-to-crypto: the token amount. On-ramp: the fiat amount you transfer.
payments[].commission, payments[].bankingFee, payments[].tokensToSendThe same figures for one payment.
expiresAtWhen the current quote expires (crypto-to-fiat).

For crypto-to-fiat, tokensToSend is the token amount converted for the payout, plus totalBankingFee, plus totalCommission. The fees are added on top. Crypto-to-crypto carries no commission, so tokensToSend is the sum of the payment amounts.

The server computes every fee and ignores any commission, fee or tokensToSend value sent in a request. The only fee input is bankingFeesOption on each fiat payment:

ValueWho bears the banking fee
OUR (default)You. The fee is added to tokensToSend.
SHAShared between you and the receiver.
BENThe receiver.

Errors

Errors return JSON with statusCode and message. message can be a string or an array of strings. Some errors also carry a code. Unknown fields in a request body are rejected with 400.

StatusMessage or codeWhenWhat to do
400Transaction is not in … status.The call is not allowed in the current status.Read the transaction and follow the status table.
400Trading pair … is not availableCreate or update with a token and currency that cannot be traded.Choose another token or currency.
403Token not supportedUnknown token or tokenIsoCode.Use an isoCode from GET /tokens.
400Reason must contain between 2 and 140 characters…Invalid payment reason (non-GBP payout).See the reason rules in Crypto-to-Fiat.
400Reason must not exceed 18 characters…Invalid payment reason for a GBP payout.As above.
400Minimum payment amount is $…A USD payment is below your organisation's minimum.Raise the amount or contact Skyline.
400Wallet with address … not found in organisationThe sending wallet is not an organisation wallet (allowance check).Add it with POST /organisations/:organisationId/add-wallet.
403Wallet not foundSame, at submit.As above.
403WALLET_NOT_WHITELISTED or a screening messageA wallet has not passed screening or ownership verification.Complete verification. See Wallets.
400Not enough balanceThe sending wallet holds less than tokensToSend.Fund the wallet and submit again.
400Delegated allowance is below the transaction totalThe token approval is too low.Approve again, then submit again.
400We are not able to process the transaction due to external market conditionsNo payment could be executed at accept.Submit again later.
400code ACCEPT_RATE_PENDINGA payment has no quote yet.Submit again, then accept.
409code ACCEPT_BINDING_MISMATCHThe transaction changed after you read its review context.Read it again and review before accepting.
500Internal server errorAccept after the quote expired.Submit again for a new quote, then accept.
401Organisation operational status does not permit this actionThe organisation is not ACTIVE.Contact Skyline.
401Your user does not have enough permission to perform this actionThe API user is not authorized.Ask an organisation admin to change the role.
403Account is not confirmedThe user account is not confirmed.Confirm the account on the platform.
403This organisation must be active in order to continue.The organisation, user or member is inactive.Contact Skyline.

Accept errors fail the transaction. Almost every accept error, including an expired quote, moves the transaction to FAILED. ACCEPT_BINDING_MISMATCH, ACCEPT_RATE_PENDING and the wallet screening errors leave the status unchanged. From FAILED, call submit again: it re-quotes and returns the transaction to WAITING_ACCEPTANCE.

A rejected create still leaves a record. If a payment is invalid, create returns 400 with the reason, and the transaction is stored as FAILED with the reason in statusInfo. Fix it with PUT /transactions/:id.

Documents

For a transaction in PROCESSING, RELEASED or COMPLETED:

  • POST /transactions/:id/invoice returns an invoice PDF.
  • POST /transactions/:id/statement returns a statement PDF.

On this page