Koywe Transactions follow a (mostly) linear path: they get created, they get paid, and they get delivered. All this is notified via webhooks (see below) or is returned when you call our APIs to follow up on an order.

The possible states for an order are:

  • WAITING: The order has been created and is waiting to be paid.

  • PENDING: The order has been paid and it’s queued for execution.

  • EXECUTING: The order is being executed. ie: The transaction is being sent to the blockchain or the bank.

  • IN_PROGRESS: Funds, either crypto or fiat, are on their way to their destination.

  • INVALID_WITHDRAWALS_DETAILS: Only valid for off ramps. The banking details were incorrect and the destination bank has rejected the transfer.

  • REJECTED: The order has been rejected by the user or the payment processor.

  • DELIVERED: The funds, either crypto or fiat, have been delivered successfully.


To make you integration easier, we have enabled ways to alert you when something happens or to define validation services before executing transactions.

You can setup a URL to recibe requests every time an event is triggered for a transaction. possible events are:

  • payment_created: order is created, Koywe awaits the payment in either fiat or crypto. The order shows a WAITING state.

  • payment_received: order is paid. Koywe confirms the payment successfully and will deliver the amountOut. Order state will be PENDING.

  • payment_rejected: order is rejected. Something happened and the order has been rejected. This event is NOT sent for wire transfers and it’s usually due to an error on cancellation on the user’s part. Order state will be REJECTED.

  • payment_expired: order is expired. Only sent for wire transfers. The user took too long to transfer or Koywe couldn’t detect the payment on time. Another order or quote should be created to secure a price. Order state will be REJECTED.

  • payment_received_modified_order_amount: This webhook event is triggered when an order is modified due to a received bank deposit. Currently, this event only applies to bank transfers made in Peru. When a user makes a bank deposit, the deposited amount may be greater or less than the original order amount. In this case, the order is automatically adjusted to the received amount. To modify the order, a new quote is generated based on the received amount, and the order is updated with this new quote. This way, the order will reflect the correct amount after the bank deposit. This event also sends an object where the original order (originalOrder) and the recalculated order (orderAdjustedByBankIncome) will be received.

Specific on ramp events:

  • crypto_tx_sent: crypto transaction is sent after fiat payment is confirmed. Order state will be IN_PROGRESS.

  • crypto_delivered: crypto transaction nis confirmed by the blockchain. Order state will be DELIVERED.

  • crypto_tx_failed: crypto transaction failed. This shouldn’t happen, but does in some minor cases (1 out of 10 thousand). If it were to happen, we would retry the transaction. Order state will be REJECTED.

Specific off ramp events:

  • fiat_sent: fiat transfer is sent from Koywe’s account. Order state will be IN_PROGRESS.

  • fiat_delivered: fiat is delivered, according to Koywe’s bank. Order state will be DELIVERED.

  • invalid_withdrawals_details: the fiat transfer was attempted, but the bank rejected the transaction because the account or the owner of the account didn’t match existing records. There’s a special endpoint to correct these details. Check out the API docs for more information. Order state will be INVALID_WITHDRAWALS_DETAILS.

  • refund_started: The request for a crypto refund has been initiated due to errors in the fiat transfer details. Order state will be REFUND_STARTED.

  • refund_in_progress: The process of sending the crypto refund has begun. Order state will be REFUND_IN_PROGRESS.

  • refund_delivered: The crypto refund has been successfully sent and confirmed. Order state will be REFUND_DELIVERED.

Additionally, you weill be able to defined a secret to validate our requests. Using this secret, we encrypt the request parameters and add the hash for you to verify:

const signature = crypto
  .createHmac('sha256', secret)

Example of a request:

  "eventName": "crypto_delivered", // event, from the list above
  "orderId": "5a3288ea-0bd4-44d2-af11-aae1f88bbd61", // order id
  "externalId':"34ae1f88bbd6" // This attribute is returned only if the 'externalId' was specified during the order creation process.
  "timeStamp": "2022-10-26T22:36:41.658Z",
  "signature": "ed40d34ab7a587cf1a16338a16cc7765ae4420936677482bb33f5f738e4f7189" // hash

KYC Webhooks

If you’re using our KYC process, you will also receive webhook notifications when a user starts and completes a KYC verification. For now, these will be sent to the same url as the operational webhooks.

  • verification_started: The user clicks the button to start the ID verification. The KYC status of the user will go from notVerified to processing.

  • verification_inputs_completed: User has finished uploading the information and submitting the forms, so we start processing all the data. The KYC status of the user will remain in processing.

  • verification_completed: We have finished analysing the user’s data and have a result. The KYC status of the user will go to either verified or reviewNeeded. For the latter, our compliance team has to complete a manual review to bring the user to verified or rejected.

  • verification_expired: The user hasn’t finished within 30 minutes of starting the process. The KYC status will go back to notVerified.

Example of a request:

  "eventName": "verification_started", // event, from the list above
  "orderId": "KYC_EVENT",
  "timeStamp": "2022-10-26T22:36:41.658Z",
  "data": "",
  "signature": "ed40d34ab7a587cf1a16338a16cc7765ae4420936677482bb33f5f738e4f7189" // hash

Authentication Callback

You can setup a URL to your API for us to confirm a user is allowed to operate in your specific context. Every time a user wants to buy, sell, or check private information associated to your environment, we will send a request to this URL before proceeding.

Our platform assumes your API will respond something that looks like a boolean and supports many authentication methods.

We will default to use the POST method. If you want other methods, please let us know.

An example using pseudo code:

// user buys crypto
const createOrder (payload) {
    if (!validUser( throw new Error('user not allowed');
    else continue
// we check with your API before proceeding
const validUser(email) {
    const payload = { email }
    const headers = { 'Authorization': 'Bearer superJWT' }
    const { data } = await, payload, { headers })
    reurn data

Webhooks Document number update

If you made a document number change, we will perform an asynchronous validation of the person’s data. The results of that validation will be sent through webhooks

UPDATE_DOCUMENT_NUMBER: After a data validation, you will receive an event with this name where there will be a confirmation message, or an error message because there was a discrepancy in the user’s data.

  • document_number_sucessfully_updated: The data verification process finished with success and the document number has been updated.

  • document_number_failed_to_update: There was an error during the verification flow, ie: “Account does not have any personal info”, “Account not found”, “There is incompatible personal info between the account and metamap”.

Example of a request:

"eventName": "document_number_sucessfully_updated", // Or "document_number_failed_to_update" if failed,
"timeStamp": "2022-10-26T22:36:41.658Z",
"data": {
"email": {{email}},
"message": {{error or success message}},
"details": "The date of birth in the account ({date}) doesn't match the one in metamap ({date}). || Name mismatch detected. Expected: {name}. Found: {name}"
"signature": "ed40d34ab7a587cf1a16338a16cc7765ae4420936677482bb33f5f738e4f7189" // hash

Webhooks Deposits

This event will notify the clients if a deposit arrives to our system from a user that has no current orders.

NEW_DEPOSIT_REGISTERED: When processing the deposit received, and associating it to a user, if this user has no current orders it will remain in his balance, at this moment this event will be fired.

  • deposit_without_order.

Example of a request:

  "eventName": "deposit_without_order",
  "timeStamp": "2024-06-07T16:17:54.258Z",
  "data": {
    "documentNumber": "{{documentNumber}}",
    "currency": "ARS",
    "clientId": "65c4ed085f193b99de04f061",
    "accountBalanceAmount": 50000,
    "email": "{{email}}"
  "signature": "ae0e02ae78ef00c5b2c623d3abf41c2785434af0bbba55e2371b1fa38c6b63f3"