Moving from the v1 API
This page is for you if your integration calls the v1 API: /api/wallet/create, /api/wallet/list or /api/withdraw, with an x-api-key header.
v1 stops at the cutover. From that moment the v1 API no longer answers, v1 keys no longer work, and v1 callbacks are no longer sent. There is no period in which both versions run. Your integration has to be on v2 at the cutover.
Your account and your funds move by themselves. Only your code has to change.
What carries over by itself
Section titled “What carries over by itself”You do nothing for these.
| In v2 | |
|---|---|
| Account and sign-in | The same email and password. |
| Profile | Your business and contact details. |
| Master wallets | The same addresses, shown in the dashboard under Balances. |
| Deposit wallets | They are reusable deposit addresses now, with the same addresses and the same names. Payments sent to them are still received. |
| Balances | Unchanged. |
| History | Your earlier transactions, shown under Payments and marked as imported. |
What your integration must do
Section titled “What your integration must do”- Call the v2 API, directly or with the Node SDK. The base URL is
https://api.ctpeway.comand every path starts with/v1. See Requests and responses. - Create a v2 API key. v1 keys do not work in v2. Create a secret key in the dashboard under Developers → API keys and send it as
Authorization: Bearer <key>, not inx-api-key. See API keys. - Add a v2 webhook endpoint. The callback URL of your v1 key is not used by v2. Register your URL under Developers → Webhooks, or with POST/v1/webhook_endpoints, and store its signing secret. See Webhooks.
- Verify the signature. v1 callbacks were not signed. Every v2 delivery has a
Ctpeway-Signatureheader, and your handler should reject a delivery that does not verify. See Verify the signature. - Handle the v2 event names and payloads. v2 sends named events in a common envelope, with different field names from the v1 callback. See The deposit webhook below.
v2 has a test mode with its own keys and a payment simulator, so you can build and test all of this without real funds.
From v1 calls to v2 calls
Section titled “From v1 calls to v2 calls”| What you do | v1 | v2 |
|---|---|---|
| Authenticate | x-api-key: <key> |
Authorization: Bearer <key> |
| Create a wallet for a customer | POST /api/wallet/create |
POST/v1/deposit_addresses |
| List wallets | GET /api/wallet/list |
GET/v1/deposit_addresses |
| Withdraw | POST /api/withdraw |
POST/v1/payouts |
| Hear about a deposit | The pending callback |
deposit.payment_detected, then deposit.payment_confirmed |
Four differences to plan for:
- Amounts are whole numbers of the asset’s smallest unit, written as strings, in requests and in events. Convert with the asset’s
decimals. See Amounts. - A key cannot pay out until you allow it. Your v2 key needs the payout permission, granted when you create the key, and a limit on where it can send money. In v1 a withdrawal could go to any address on the chain. To keep that in v2, restrict the key to the IP addresses of your servers: it then pays any address that is valid on the payout’s network, and only in requests from those addresses. If you always pay the same few addresses, list them as the key’s payout allowlist instead, or use both. See API keys.
- A payout needs an
Idempotency-Keyheader, and it is checked before it is accepted. If it cannot be made, you get an error at once instead of finding out later. Its progress is then reported in events. See Idempotency. - Networks and assets are named by the v2 API. GET/v1/networks and GET/v1/assets list the names to use.
The deposit webhook
Section titled “The deposit webhook”In v1 you received a callback with the status pending when a deposit was seen. In v2 the same deposit produces events:
| Event | Meaning | What to do |
|---|---|---|
| deposit.payment_detected | The deposit was seen on the network. Not final. | Show it as pending, if you want to. |
| deposit.payment_confirmed | The deposit is confirmed. | Credit your customer. |
| deposit.payment_released | The funds have been moved to your master wallet. | Nothing. |
| payment.orphaned, payment.dropped | A deposit that was seen is no longer on the network. | Undo anything you did for it. |
The fields you used in the v1 callback are in data.object of the event:
| v1 callback | v2 event |
|---|---|
chain |
payment.network |
txHash |
payment.tx_hash |
token |
payment.asset |
from, to |
payment.from_address, payment.address |
name |
deposit_address.name |
amount |
payment.amount, in the asset’s smallest unit, with payment.decimals |
usdAmount |
payment.usd_value |
blockHeight |
payment.block_height |
status |
The event type, as in the table above. |
Each event’s page under Webhook events lists every field.
What else is different
Section titled “What else is different”Held deposits. The platform fee is 0.5% of each deposit, charged in the network’s native coin. A deposit in a token has its fee paid by your master wallet, and waits in its address, Held, until the master wallet can pay. In v2 you can see what is held and what it needs, in the dashboard and through the API. Funds that are in a deposit address at the cutover are settled by v2 under this rule. See Settlement and the platform fee.
Signing in. You sign in again once after the cutover. A code sent by WhatsApp keeps working as your second factor, and you can add an authenticator app.
New in v2. Invoices with a hosted checkout page, test mode, signed events with a log you can replay, and a Node SDK and command-line tool.

