Skip to main content

Funding your account

There are two rails for getting money in, each with its own endpoint:

RailEndpointReturns
CryptoGET /v2/deposit-addressesone address per (asset, chain) pair
Fiat (CLP)GET /v2/deposit-instructionsSkipo's bank accounts

Both are read-only and take a Tier-1 key with the transfers:read scope.

:::danger Not to be confused with GET /v2/contacts /v2/contacts lists your withdrawal destinations — addresses Skipo can send money to. The endpoints on this page list Skipo addresses and accounts that you send to.

Sending funds to a contact's address does not credit your account. :::

Crypto deposits​

GET /v2/deposit-addresses?assetSymbol=USDT
[
{
"assetSymbol": "USDT",
"networkId": "TRON",
"networkName": "Tron (TRC20)",
"address": "TDunhSa7jkTNuKrusUTU1MUHtqXoBPKETV",
"tag": null,
"tagRequired": false,
"tagType": null,
"tagLabel": null
}
]

Addresses are stable: Skipo does not rotate or retire them, and reading this endpoint never mints a new address, so re-reading is free and safe.

Key any cache on the pair (assetSymbol, networkId), never on the address string — it is not unique across chains and is the one field with no identifier beside it.

An (asset, chain) pair with no address yet is simply absent from the list rather than listed empty. It appears once provisioning completes.

Only assets you have added are listed​

:::info An empty list almost never means "you have no address" Skipo pre-provisions an address for effectively the whole catalogue before you ask for any of it, so that it is ready when you want it. An address existing therefore does not mean you use that asset — and listing all of them would bury the two or three you actually fund.

So this endpoint returns only the assets the account has added. An asset you have not added answers [], even if you name it with ?assetSymbol=. That is deliberate: asking for one asset must not reveal an address the listing hides.

How to enable it: add the asset in the Skipo app, or open the deposit screen and select it. That is all it takes. The address already exists and does not change, so it appears here on the next call — nothing new is minted and nothing you already had is lost. :::

This is the same set GET /v2/balances returns, so the two endpoints always agree about which assets are yours. A useful rule: if the asset appears in /v2/balances, its address appears here.

Three ways a deposit is lost​

1. Sending over the wrong chain. An address is only valid on the networkId it is listed under. Several chains share an address format — every EVM chain does — so the same string can appear under more than one networkId. Skipo credits only the chains it sweeps: funds sent over another one are not recoverable.

2. Omitting the tag. Where tagRequired is true, the tag is mandatory. On those chains Skipo shares one address across accounts and the tag is the only thing identifying yours; a deposit without it does not credit automatically.

3. Sending a different asset. An EVM address is shared by every token on that chain. Sending USDC to the address you read for USDT — same string, different token — parks the funds for manual review, again with no row, no webhook and no error. Send only the exact assetSymbol on that row, and if a pair you expect is missing, do not use a neighbouring row's address.

Read tagRequired, not tag !== null — it is a property of the chain, not of that row. tagType tells you which of the two names the chain uses (MEMO or DESTINATION_TAG) as a machine key; tagLabel is the display text.

Minimums and fees​

Not on this resource. They live on GET /v2/assets:

  • networks[].minimumDeposit — the smallest amount that will be credited
  • asset-level minimumDeposit — the platform floor

:::danger A deposit below the minimum is not rejected — it is lost It is not rejected, not returned and not queued. It is swept to a Skipo internal account: you will see no deposit, no webhook and no error, and recovery is a manual support case.

Read minimumDeposit from GET /v2/assets before every send and leave a margin. This resource publishes no amounts, deliberately. And do not send dust as a connectivity test. :::

Deposits carry no fee. The fee returned by GET /v2/deposits is always "0" and exists only for shape parity with withdrawals.

Fiat deposits (CLP)​

GET /v2/deposit-instructions
[
{
"assetSymbol": "CLP",
"accountHolder": "Skipo Chile SpA",
"accountHolderNationalId": "77.777.777-7",
"bankName": "Banco de Chile",
"accountType": "Cuenta Corriente",
"accountNumber": "00-123-45678-90",
"accountEmail": "deposits@skipo.com",
"mustMatchSenderNationalId": true
}
]

:::danger The transfer must come from an account in your own name Skipo matches an incoming transfer to an account by the originator's national id (in Chile, the RUT) as the bank reports it. There is no reference code, no memo and no per-customer account number to fall back on.

A transfer sent from someone else's account — a company account for a personal balance, a partner, a friend, a payment processor — cannot be matched and is not credited. Recovering it is a manual support case, not an automatic return.

Check which id Skipo holds for you with GET /v2/account (nationalId). :::

accountHolderNationalId is Skipo's id — it identifies the destination. It is not the id the money has to come from; that one is yours.

More than one account is returned, and they are interchangeable​

A deposit into any of them credits the same balance. They exist to spread volume across banks, not to segregate funds.

Prefer the one at the bank you already use: in Chile a same-bank transfer settles faster and costs less than an interbank one.

CLP only​

There is no USD bank account. GET /v2/deposit-instructions never returns one, and GET /v2/deposit-addresses?assetSymbol=CLP returns [] — fiat has no chain. To fund in USD, use the crypto rail.

Reconciling a deposit that has arrived​

GET /v2/deposits publishes, at the top level, who sent the money and when it moved:

{
"id": "dp_01HZY0...",
"assetSymbol": "CLP",
"amount": "500000",
"status": "COMPLETED",
"createdAt": "2026-08-20T14:04:42.000Z",
"senderName": "JANE DOE",
"senderNationalId": "12.345.678-9",
"senderBankId": "BANCO_DE_CHILE",
"senderAccount": "00-123-45678-90",
"bankReference": "987654321",
"bankDescription": "transferencia",
"valueDate": "2026-08-20T14:03:11Z"
}

All are null on a crypto deposit, and on a fiat deposit an operator entered by hand.

senderNationalId means "this was you" — and its null means nothing​

It is published only when it matches your own. So a populated value is positive confirmation that the deposit was self-funded, and is never a counterparty's identifier.

A null does not say "a third party sent this"

The match is a necessary, not a sufficient condition. The field arrives null in all of these, and the response cannot tell them apart:

  • a sender who genuinely is a third party;
  • every crypto and internal deposit;
  • a fiat deposit an operator entered;
  • the originator's id missing from the bank's message;
  • Skipo being unable to read your own nationalId — the lookup is per request and fails soft, so a transient outage leaves the field null on every row, self-funded ones included.

Never surface a null as "someone else sent this", and do not branch reconciliation or compliance logic on it.

If you compare it to GET /v2/account's nationalId, normalise first: strip every character outside [0-9kK] and upper-case. What is published is the sender's raw string in the bank's own formatting — a byte-for-byte comparison fails on real rows.

valueDate is not createdAt​

valueDate is when the money moved; createdAt is when Skipo credited it. On production the median gap between the two is 91 seconds, and they fall in different months about once in 27 months.

To close a month, use valueDate and join to the ledger on source.id.

:::caution Date filters bound createdAt startDate and endDate on GET /v2/deposits and GET /v2/ledger filter on creation time, not value date, and there is no filter or sort on valueDate at all. A month-end query has to read valueDate back and bucket client-side.

Two further traps in the same parameters:

  • Both bounds are inclusive. Use the next period's start as your end and exclude it yourself, or a deposit landing exactly on the boundary is counted in both months.
  • Send full timestamps with an explicit Z. A date-only 2026-08-31 is read as midnight and silently drops the whole 31st. Use 2026-08-01T00:00:00Z … 2026-09-01T00:00:00Z. :::

bankReference​

The operation number the bank assigned the movement. Present on nearly every automated deposit, absent on operator-entered ones. Free-form and short — treat it as an opaque string for reconciliation, never parse it.

bankDescription is a different thing: the free text the sender put on the transfer, and it is empty on most deposits.

Finding out without polling​

A deposit emits deposit.created. The webhook payload carries the same fields as GET /v2/deposits, sender attribution included.

⚠️ An automated fiat deposit emits exactly one webhook: it is born COMPLETED, so no deposit.status.updated follows.

See also: Webhooks · Withdrawal fees · Deposits · Assets.