Migrating from v1 to v2
Bank-transfer on-ramp and off-ramp orders are moving from API v1 to API v2 (RAMP), and API v1 will be shut down soon. If your integration calls the v1 /v1/otc/deposit/, /v1/otc/withdraw/ or /v1/order/info/… endpoints, this guide lists everything you need to change.
API v2 is not a drop-in replacement: paths, field names, tickers and some flows are different, and there is no compatibility layer for the old request shapes. API v1 and API v2 run side by side on different hosts until v1 is shut down, so you can migrate and test at your own pace.
1. What changes at a glance
- New base URL with no
/v1prefix and no trailing slashes. A path ending in/returns404. - One endpoint per direction:
POST /onramp(fiat → crypto) andPOST /offramp(crypto → fiat). - No
from_asset/to_asset. Every order has one cryptoassetand onefiat_currency. The direction is decided by the endpoint you call. - New tickers. The
-ARKsuffix is gone. Arkade assets have their own tickers (ARK-BTC,USDT-CX). PlainBTCandUSDTnow mean on-chain Bitcoin and Tron. - Amounts are decimal strings (
"100.00","0.00250000"), not JSON numbers. - KYC goes through ID-Flow. Orders at or above the KYC threshold need a customer verified with ID-Flow under the order's email address.
- Off-ramp needs a new call. After sending the crypto, report the transaction id with
POST /order/{token}/deposit. Until you do, the order is not credited automatically. - Off-ramp: pay exactly the returned amount. API v2 may lower the requested
crypto_amountslightly (by up to 1000 base units), and the deposit must match the returned value exactly. - SWIFT payouts now take the IBAN, BIC and a structured beneficiary address as separate fields.
- Errors are
{"error": …}with a proper HTTP status (400/403/404/409/422/502). KYC and minimum-order refusals are403. - Not in API v2: crypto↔crypto swaps and trades, order cancellation, vouchers, and the
ref/sub_idpartner fields.
2. Environments and base URLs
| Environment | API v1 (old) | API v2 (new) |
|---|---|---|
| Production | https://api.outlogic.net/v1 | https://api.ramp.outlogic.net |
| Staging | https://api.staging.outlogic.net/v1 | https://api-staging.ramp.outlogic.net |
| Other URLs | Production | Staging |
|---|---|---|
| API v2 reference | These docs | These docs |
| ID-Flow identity verification (KYC) for customers | Your own ID-Flow instance (agreed with us during onboarding) | https://demo-staging.idflow.ch/ |
Staging is a sandbox: bank payments and crypto transfers there use test networks and never move real money. Use it for all integration testing.
3. Endpoint map
| API v1 (old) | API v2 (new) | Notes |
|---|---|---|
GET /v1/assets/ | GET /assets | Same top-level keys. Each asset now has a network label. |
| (none) | GET /limits | Minimum order and KYC threshold, per currency. |
POST /v1/otc/deposit/ | POST /onramp | Fiat → crypto. See section 7. |
POST /v1/otc/withdraw/ | POST /offramp | Crypto → bank (sepa/swift/us). See section 8. |
| (none) | POST /order/{token}/deposit | New and required for off-ramp. See section 9. |
GET /v1/order/info/{id}/ | GET /order/{token} | Uses the id returned at creation. See section 10. |
GET /v1/otc/markets/, POST /v1/otc/trade/, POST /v1/order/, /v1/order/cancel/… | Not available | Crypto↔crypto trading and cancellation are not part of API v2. Contact us if you depend on them. |
sub_id, ref, expo_token_str | Not accepted | No partner sub-accounts, referral codes or push tokens. |
4. General conventions
- JSON request and response bodies (
Content-Type: application/json), with snake_case field names. - No authentication on the endpoints in this guide. CORS is open (
Access-Control-Allow-Origin: *, no credentials), so you can call them from a browser or a server. Order creation is rate limited per client IP (section 11). - Amounts are strings in plain decimal notation, with at most 8 decimals and never more than the asset has (
USDT/USDT-CXhave 6). Don't send scientific notation such as1e-7. - Optional
originon create calls. Leave it unset (it defaults toapi). The oldmobilevalue is not accepted. - Order identifiers:
idis the short public token (use it for status and deposit reporting),codeis a human-readable reference such asORD-XXXX-XXXX(quote it to support), andtransfer_codeis the bank payment reference. - Timestamps are ISO 8601 (
2026-10-23T09:15:00.000Z). API v1 used2026-10-23 09:15:00. - No quote step and no locked rate. The rate is set when the payment arrives, so the other leg of the trade is
nulluntil settlement (sections 7 and 8).
5. Assets, tickers and currencies
Read the live list from GET /assets rather than hardcoding it. Each entry in from_assets has symbol, name, precision (decimals) and network, which names the address format destination_crypto_address must use. to_assets lists the fiat currencies.
| Ticker | Network | Payout address format | Decimals | API v1 equivalent | Notes |
|---|---|---|---|---|---|
ARK-BTC | Arkade | ark1… / tark1… | 8 | BTC-ARK | Near-instant payout. |
USDT-CX | Arkade | ark1… / tark1… | 6 | USDT-ARK | Near-instant payout. |
CEXT | Arkade | ark1… / tark1… | 8 | — | |
BTC | Bitcoin | 1… / 3… / bc1… (tb1… on staging) | 8 | on-chain BTC | On-ramp payout on-chain, usually within a few minutes. |
USDT | Tron | T… | 6 | USDT (Tron) | On-ramp payouts take longer: they are not automated yet. |
Fiat currencies: EUR, CHF, USD.
The ticker sets the payout network. An Arkade address with BTC is refused (destination_crypto_address is not a valid Bitcoin address). If you used BTC-ARK on API v1, send ARK-BTC on API v2. Responses echo the ticker you sent.
Off-ramp deposits must be Arkade transfers. The off-ramp deposit address is always an Arkade address, and only an Arkade transfer to it is credited. Use ARK-BTC or USDT-CX for off-ramp. On-chain Bitcoin or Tron deposits are not credited automatically.
6. Limits and KYC (ID-Flow)
GET /limits returns the minimum order and the KYC threshold. Both are set in CHF and also converted into each fiat currency at the rates the server uses for its own check. The values can change without notice, so read them at runtime instead of hardcoding them.
{
"min_order_chf": "20.00",
"kyc_threshold_chf": "1000.00",
"by_currency": {
"EUR": { "min_order": "...", "kyc_threshold": "..." },
"CHF": { "min_order": "20.00", "kyc_threshold": "1000.00" },
"USD": { "min_order": "...", "kyc_threshold": "..." }
}
}
- Below the minimum: refused with
403,{"error": "Minimum order amount is CHF 20"}. - Below the KYC threshold: no identity verification is needed.
- At or above the KYC threshold: the customer must have completed identity verification with ID-Flow, using the same email address you send as
emailon the order. The check happens when the order is created. A customer who isn't verified is refused with403,{"error": "KYC verification required for orders over CHF 1000"}. Send them to your ID-Flow instance to verify (on staging, the shared one in section 2), then create the order again. - The order's
kyc_verifiedfield istruewhen its ID-Flow verification was confirmed. - SEPA payouts for verified customers go to the IBAN registered in the customer's ID-Flow profile, which takes precedence over the IBAN in the request (section 8).
- API v1 reported KYC failures as a
messageorkycErrorfield. On API v2, check for HTTP403instead.
The values above are illustrative. Use what the endpoint returns.
7. On-ramp: fiat → crypto
POST /onramp replaces POST /v1/otc/deposit/. The customer pays fiat by bank transfer and receives crypto at destination_crypto_address.
Request field mapping
API v1 /v1/otc/deposit/ | API v2 /onramp | Notes |
|---|---|---|
from_asset ("EUR") | fiat_currency | Required. EUR, CHF or USD. |
to_asset ("BTC-ARK") | asset | Required. An API v2 ticker such as ARK-BTC (section 5). |
from_amount (number) | fiat_amount (string) | Required. The amount the customer will pay. |
email | email | Required. Also used for the KYC check (section 6). |
destination_address | destination_crypto_address | Required. Validated against the ticker's network. |
refund_address | refund_address | Optional. |
ref, sub_id, expo_token_str | — | Not accepted. |
Before: API v1
POST https://api.outlogic.net/v1/otc/deposit/
Content-Type: application/json
{
"email": "jane@example.com",
"from_asset": "EUR",
"from_amount": 250,
"to_asset": "BTC-ARK",
"destination_address": "ark1qq…"
}
After: API v2
POST https://api.ramp.outlogic.net/onramp
Content-Type: application/json
{
"email": "jane@example.com",
"fiat_currency": "EUR",
"fiat_amount": "250.00",
"asset": "ARK-BTC",
"destination_crypto_address": "ark1qq…"
}
Response (201 Created)
{
"order": {
"id": "k8d2m4x9qa", // public token: use it for GET /order/{token}
"code": "ORD-7KQ2-M9XD",
"email": "jane@example.com",
"direction": "onramp",
"asset": "ARK-BTC",
"fiat_currency": "EUR",
"fiat_amount": "250.00",
"crypto_amount": null, // set when the payment arrives
"status": "WAITING_FOR_DEPOSIT",
"transfer_code": "k3x9q2m7ab",
"destination_type": "crypto",
"destination_crypto_address": "ark1qq…",
"deposit_txid": null,
"deposit_iban": "LT00 0000 0000 0000 0000", "deposit_bic": "XXXXLT21XXX",
"deposit_beneficiary": "…", "deposit_bank_name": "…", "deposit_payment_type": "sepa",
"kyc_verified": false,
"created_at": "2026-10-23T09:15:00.000Z",
"expires_at": "2026-10-25T09:15:00.000Z",
"completed_at": null
},
"bank_details": {
"iban": "LT00 0000 0000 0000 0000", "bic": "XXXXLT21XXX",
"beneficiary": "…", "bank_name": "…",
"payment_type": "sepa", "transfer_code": "k3x9q2m7ab"
},
"fee": { "feePct": "0.01", "feeFlat": "0", "feeAmount": "2.50", "netAmount": "247.50" }
}
- Show the customer
bank_details. These replace API v1'sdeposit_sepa_*/deposit_swift_*fields. The bank details above are placeholders: always use what the API returns, as the receiving account can differ by currency. - The customer must put
transfer_codein the payment reference. It is 10 lowercase letters and digits, with no prefix or separators. Without it the payment can't be matched to the order automatically. crypto_amountstaysnulluntil the payment is confirmed. The trade is priced then, fees are deducted, and the crypto is sent. Show it as "to be determined at the market rate when your payment arrives", not as a quote.- The customer must pay by
expires_at. Later payments are not credited automatically.
8. Off-ramp: crypto → fiat
POST /offramp replaces POST /v1/otc/withdraw/. The customer sends crypto and receives a bank payout. destination_type must be sepa, swift or us. Crypto payouts (destination_type: "crypto") are not part of the off-ramp.
Common fields
API v1 /v1/otc/withdraw/ | API v2 /offramp | Notes |
|---|---|---|
from_asset ("BTC-ARK") | asset | Required. Use ARK-BTC or USDT-CX (section 5). |
to_asset ("EUR") | fiat_currency | Required. |
from_amount (number, in BTC) | crypto_amount (string) | Required, in whole units: "0.00250000" = 250,000 sats. |
email | email | Required. Also used for the KYC check (section 6). |
destination_type | destination_type | Required: sepa | swift | us. |
refund_address, expo_token_str | — | Not accepted. |
Destination fields by type
| Type | API v1 | API v2 |
|---|---|---|
sepa | destination_bank_address = IBANdestination_bank_name = account holder | destination_bank_address = IBANdestination_bank_name = beneficiary nameUnchanged. For a customer verified with ID-Flow, the IBAN in their ID-Flow profile is used instead of the submitted one. |
swift | destination_bank_address = BICdestination_bank_account_number = IBANdestination_bank_name = account holder | destination_bank_address = IBAN (changed meaning)destination_bic = BIC (new)destination_bank_name = beneficiary namedestination_country (ISO 3166-1 alpha-2, e.g. CH)destination_street_name, destination_building_number, destination_town_name, destination_post_codeAll required. The beneficiary address is now structured, not free text. |
us | destination_bank_account_numberdestination_bank_routing_number | destination_bank_account_numberdestination_bank_routing_number (ABA checksum validated)destination_bank_name = beneficiary name (now required)US payouts take longer: they are not automated yet. |
SWIFT is the easiest change to miss: destination_bank_address held the BIC on API v1 and holds the IBAN on API v2.
Example
POST https://api.ramp.outlogic.net/offramp
Content-Type: application/json
{
"email": "jane@example.com",
"asset": "ARK-BTC",
"fiat_currency": "EUR",
"crypto_amount": "0.00250000",
"destination_type": "sepa",
"destination_bank_address": "DE89370400440532013000",
"destination_bank_name": "Jane Doe"
}
201 Created
{
"order": {
"id": "p4z7c1n8wd", "direction": "offramp", "asset": "ARK-BTC",
"crypto_amount": "0.00249873", // pay EXACTLY this amount (see below)
"fiat_amount": null, // set when the payout is priced
"status": "WAITING_FOR_DEPOSIT", "destination_type": "sepa",
"expires_at": "2026-10-25T09:15:00.000Z"
// …
},
"deposit_crypto_address": "ark1qq…", // send the crypto here
"fee": { "feePct": "0.01", "feeFlat": "0", "feeAmount": "…", "netAmount": "…" }
}
- Send exactly
order.crypto_amount, not the amount you requested. Each open order gets a unique amount, the request or up to 1000 base units (sats, or 0.001 USDT) below it, so a deposit can only match its own order. A deposit that doesn't match exactly is not credited. - The crypto must be an Arkade transfer to
deposit_crypto_address, sent after the order was created. Then report it (section 9). fiat_amountstaysnulluntil the deposit is credited. The payout is priced then and rounded down to whole cents.
9. Reporting an off-ramp deposit (new step)
API v1 had no equivalent. Once the Arkade transfer has been sent, tell API v2 which transaction paid the order:
// POST /order/{token}/deposit
{ "txid": "3f1c…e9a0" } // 64 lowercase hex characters (the Arkade txid)
The transaction is verified before anything is credited. It must be an Arkade transfer that pays the deposit address exactly the order's crypto_amount of the order's asset, it must not be older than the order, and it must not have been credited to another order. On success the order moves to DEPOSIT_RECEIVED and the payout follows.
| Status | Meaning and what to do |
|---|---|
200 | Credited. Returns {"order": …}. Reporting the same txid again returns the same order unchanged, so retries are safe. |
404 | No order with that token. |
409 | The order isn't an off-ramp order waiting for a deposit, or the transaction was already credited to another order. |
422 | The transaction doesn't pay this order: wrong amount, wrong asset, not to the deposit address, older than the order, or not an Arkade transfer. |
502 | The transaction couldn't be verified right now. Retry later with the same txid. |
If a deposit is never reported, the order stays in WAITING_FOR_DEPOSIT until our support team matches it by hand. Report the txid as soon as the transfer is sent, and keep retrying on 502.
10. Order status
GET /order/{token} replaces GET /v1/order/info/{id}/. Both wrap the order in {"order": …}. The token is the id returned when the order was created. Orders created on API v1 are not visible on API v2 (section 12).
Response field mapping
| API v1 order field | API v2 order field | Notes |
|---|---|---|
id | id | Short public token on both. |
| — | code, direction | direction is onramp or offramp. |
from_asset / to_asset | asset, fiat_currency | |
from_amount, deposit_amount | fiat_amount, crypto_amount | The customer-declared leg is set at creation. The other is null until settlement. |
deposit_sepa_address / deposit_swift_address / deposit_bank_address | deposit_iban | On-ramp receiving account. |
deposit_sepa_bic / deposit_swift_bic | deposit_bic | |
deposit_*_beneficiary, deposit_*_bank_name | deposit_beneficiary, deposit_bank_name | |
| (inferred from which fields were set) | deposit_payment_type | sepa or swift. |
deposit_crypto_address | create response only | Off-ramp: returned by POST /offramp as deposit_crypto_address. |
funding.txs | deposit_txid | Off-ramp: the credited Arkade transaction. |
transfer_code, destination_type, destination_crypto_address, email, created_at, expires_at, completed_at | same names | Timestamps are now ISO 8601. |
chf, referral, sub_id, refund, confirmed_at, approved_at, … | — | Not returned. |
| — | kyc_verified | Whether the customer's ID-Flow verification was confirmed for this order. |
Status mapping
| API v1 | API v2 | Meaning on API v2 |
|---|---|---|
WAITING_FOR_DEPOSIT | WAITING_FOR_DEPOSIT | Waiting for the customer's payment. |
DEPOSIT_RECEIVED | DEPOSIT_RECEIVED | Payment confirmed. Settlement is starting. |
DEPOSIT_CONFIRMED, APPROVED, PROCESSING | PROCESSING | The payout is being made. |
COMPLETED | COMPLETED | Paid out. Final. |
| — | PENDING_MANUAL | New. Our team is reviewing the order before paying out, for example for large amounts. Not a failure. Keep polling. |
| — | FAILED | New. Settlement failed. Contact support with the order code. |
EXPIRED | EXPIRED | May not be set automatically, so treat expires_at as the deadline. |
REJECTED, CANCELLED | REJECTED | Final. API v2 has no customer cancellation. |
REFUNDED | REFUNDED | Final. |
There are no webhooks to partners. Poll GET /order/{token}, for example every 30–60 seconds while the customer is waiting and less often afterwards. Bank transfers can take several days.
11. Errors and rate limits
Errors are always {"error": …}. For most errors it's a readable string naming the field the request carried, e.g. {"error": "destination_bic is required for swift payouts"}. When the request body doesn't match the schema of /onramp or /offramp, it's an object with the problems per field: {"error": {"formErrors": [], "fieldErrors": {"fiat_amount": ["Invalid"]}}}.
| Status | When |
|---|---|
400 | Invalid request: missing or malformed field, unknown ticker or currency, wrong address format for the ticker, invalid IBAN/BIC/ABA number, too many decimals. |
403 | Below the minimum order, or KYC required (section 6). |
404 | Unknown order token, or a wrong path (including a trailing /). |
409 / 422 / 502 | Deposit reporting (section 9). |
429 | Rate limit exceeded. Back off and retry. |
| Endpoint | Rate limit per client IP |
|---|---|
POST /onramp, POST /offramp, POST /order/{token}/deposit | 30 per minute |
If you create orders from a server on behalf of many customers, all of them count against your server's IP. Contact us before launch if you expect to go above this limit.
12. Switching over
API v1 will be shut down soon, so plan a one-way move to API v2.
Before switching
- Implement the API v2 calls: the new paths (no
/v1, no trailing slashes), the ticker mapping (section 5), amounts as strings, the new SWIFT fields, error handling by HTTP status, and the off-ramp deposit report (section 9). - Read assets and limits from
GET /assetsandGET /limitsinstead of hardcoding them. - Make sure customers who will order at or above the KYC threshold verify with ID-Flow using the email address you send on the order.
- Test end to end on staging (
https://api-staging.ramp.outlogic.net): one on-ramp, and one off-ramp including the deposit report.
Switching
- Send all new orders to
https://api.ramp.outlogic.net. - Orders are not migrated. Orders already created on API v1 finish there: customers who already have v1 payment instructions pay against those, and you look those orders up on API v1 until they are final or past their
expires_at. Plan for this before API v1 shuts down. - Stop creating orders on API v1.