TuCOPRamp

Deposits

Give a party a reusable Bre-B key: the pesos it pays there arrive as USDC on Celo in its wallet.

With the deposit product, a party pays Colombian pesos from its own bank and receives USDC on Celo in its wallet. TuCOP gives the party a deposit account: a Bre-B key, issued by the provider for that party, tied to one wallet. The account is created once per wallet and reused.

Every request needs your consumer key and a party session. Opening a deposit account (POST /deposits) also needs the deposit product, or it gets 403 product_not_enabled, and a party that finished identity verification. Reading your accounts (GET /deposits, GET /deposits/{id}) needs neither.

Get the deposit account

bash
curl https://api.ramp.tucop.xyz/deposits \
  --request POST \
  --header "X-TuCOPRamp-Key: $TUCOPRAMP_API_KEY" \
  --header "X-TuCOPRamp-Party-Session: $PARTY_SESSION" \
  --header "Idempotency-Key: $(uuidgen)" \
  --header "Content-Type: application/json" \
  --data '{}'
json
{
  "id": "dep_01j9z3k8q2m4n6p8r0s2t4v6w8",
  "product": "deposit",
  "status": "active",
  "deposit_instructions": {
    "rail": "bre_b",
    "currency": "COP",
    "bre_b_key": "00901234567890123456",
    "account_holder_name": "Provider account holder",
    "bank_name": null
  },
  "destination": {
    "address": "0x1111111111111111111111111111111111111111",
    "chain": "eip155:42220",
    "asset": "USDC",
    "token_contract": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C"
  },
  "fee_bps": 150,
  "created_at": "2026-10-10T20:00:00.000Z"
}

Show the party deposit_instructions.bre_b_key and the holder name. Whatever it pays there, from its own bank account, arrives as USDC at destination.address, less TuCOP's fee and the provider's spread. fee_bps is TuCOP's fee in basis points. TuCOP sets it for each integrator, and it is fixed when the account is created.

The wallet defaults to the one the session was opened with. Pass wallet_address to use another wallet, or when the session was not opened with a wallet; a different wallet gets its own account. A deposit account belongs to your integrator: another integrator linked to the same party gets its own account for the same wallet, with its own fee, and neither sees the other's. Your app answers for the address: TuCOP checks only that it is one, and USDC sent to a wrong address cannot be recovered. The first call answers 201, later calls 200 with the same account.

An account the provider already has

The party may already have a deposit account at the payments provider, opened outside TuCOP. When it has an active one that takes pesos and sends USDC on Celo to the wallet_address you ask for (compared without regard to case), and no integrator holds it yet, POST /deposits takes that account over instead of opening another one. It answers 200, with that account's Bre-B key, and fee_bps is that account's own fee, which can differ from your integrator's fee. From then on it is your integrator's account like any other: later calls answer 200 with the same account. An account another integrator already holds is never taken over; you get a new one (201).

Change the wallet

To keep the same Bre-B key and send the USDC to another wallet, use PATCH /deposits/{id}:

bash
curl https://api.ramp.tucop.xyz/deposits/dep_01j9z3k8q2m4n6p8r0s2t4v6w8 \
  --request PATCH \
  --header "X-TuCOPRamp-Key: $TUCOPRAMP_API_KEY" \
  --header "X-TuCOPRamp-Party-Session: $PARTY_SESSION" \
  --header "Idempotency-Key: $(uuidgen)" \
  --header "Content-Type: application/json" \
  --data '{ "wallet_address": "0x2222222222222222222222222222222222222222" }'

It answers 200 with the account and its new destination.address. Pesos paid from then on arrive at the new wallet. The same wallet answers 200 with the account unchanged. Only the integrator that opened (or took over) the account can change it, and it needs the deposit product. wallet_address is checked as on POST /deposits, and your app answers for it. An Idempotency-Key is required, as on POST.

TuCOP refuses the change with 409 deposit_in_flight while money may still be on its way to the current wallet, so a deposit is never split between two wallets:

  • a deposit whose latest activity is funds_scheduled, funds_received, in_review or payment_submitted (the pesos arrived and the USDC was not delivered or refunded yet), or
  • any money activity on the account in the last 2 minutes.

Retry after the deposit shows payment_processed or refund in GET /deposits/{id}, and two minutes have passed. The error includes the account as deposit. No webhook is sent for the change: the answer confirms it.

Follow the deposits

GET /deposits lists the party's deposit accounts, newest first, at most 50, without activity. GET /deposits/{id} returns the account with events: the provider's activity on it, most recent first.

json
{
  "id": "dep_01j9z3k8q2m4n6p8r0s2t4v6w8",
  "product": "deposit",
  "status": "active",
  "deposit_instructions": {
    "rail": "bre_b",
    "currency": "COP",
    "bre_b_key": "00901234567890123456",
    "account_holder_name": "Provider account holder",
    "bank_name": null
  },
  "destination": {
    "address": "0x1111111111111111111111111111111111111111",
    "chain": "eip155:42220",
    "asset": "USDC",
    "token_contract": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C"
  },
  "fee_bps": 150,
  "created_at": "2026-10-10T20:00:00.000Z",
  "events": [
    {
      "id": "7c1e9a52-3f0b-4d6e-8a2c-5b9d0e1f2a3b",
      "type": "funds_received",
      "amount": { "amount": "100000.00", "asset": "COP" },
      "created_at": "2026-10-11T15:04:10.003Z"
    }
  ]
}
FieldMeaning
statusactive, or deactivated when TuCOP turned the account off. There is no endpoint to deactivate one. A deactivated account is no longer read from the provider, so it sends no more webhooks.
events[].idThe activity line. The same value is event_id in the webhook.
events[].typeThe provider's name for the activity, for example funds_received. Treat it as text: new values can appear.
events[].amountIn COP when the provider reports pesos, otherwise in USDC. null when the provider gave no amount.

GET /deposits answers the same objects without events, in data:

json
{
  "data": [
    {
      "id": "dep_01j9z3k8q2m4n6p8r0s2t4v6w8",
      "product": "deposit",
      "status": "active",
      "...": "..."
    }
  ]
}

Each activity line also arrives as a deposit.received webhook, once, to the consumer that created the account, as soon as the provider reports it. TuCOP also reads the account from the provider about once a minute while it is active, and every 30 minutes after a day without activity, so a line whose notice was missed still arrives; either way it is sent once. The webhook's destination_address says which wallet the USDC goes to.

Errors

CodeStatusWhen
invalid_wallet_address400wallet_address is not a 0x address.
product_not_enabled403Your consumer does not have deposit.
deposit_not_found404Not the party's, or not there.
deposit_in_flight409PATCH: money may still be on its way to the current wallet. See Change the wallet.
deposit_not_owned409PATCH: an older account no single integrator owns. Contact TuCOP.
deposit_deactivated409PATCH: the account is deactivated.
deposit_wallet_in_use409PATCH: you already have a deposit account for the party and that wallet.
party_not_verified409Identity verification is not finished.
wallet_address_required422The session has no wallet and none was passed.
provider_rejected422The provider refused to create or change the account.
provider_unavailable502Retry. On PATCH, also when the provider did not accept the change: the wallet stays as it was.