# Authentication Source: https://docs.rampnow.io/api-reference/authentication Authenticate and sign every Rampnow API request Every request to the Rampnow API is authenticated with your API key and an HMAC-SHA256 signature made with your API secret. Your partner account comes with an API key and secret already created. You can find them in the API Hub of the Partner Dashboard, and regenerate them there if you need to. ## Environments | Environment | API base URL | Widget host | | - | - | - | | Production | `https://api.rampnow.io` | `https://app.rampnow.io` | | Sandbox | `https://api.sandbox.rampnow.io` | `https://app.sandbox.rampnow.io` | Keys are environment-scoped: use a sandbox key against the sandbox hosts (`*.sandbox.rampnow.io`), for both the API and the widget, and a production key against the production hosts. A sandbox key will not work on production hosts, or vice versa. ## Request headers Send these headers on every request, over HTTPS: | Header | Value | | - | - | | `X-RAMPNOW-API-KEY` | Your API key from the Partner Dashboard | | `X-RAMPNOW-TIMESTAMP` | Current Unix time in **seconds** (for example `1772080882`) | | `X-RAMPNOW-SIGN` | Hex-encoded HMAC-SHA256 signature of the request. Case-insensitive | ## Sign a request Take the current Unix time in **seconds**, as a string. Use the same string in the header and in the message. Concatenate, with no separators: 1. The timestamp 2. The HTTP method in uppercase (`GET`, `POST`) 3. The full request path, starting with `/api/partner/v1`, plus the query string exactly as you send it (for example `/api/partner/v1/ext/ramp_order/blocked_currencies?country=DE`) 4. For `POST`, `PUT` and `PATCH`: the request body, byte for byte as you send it. For `GET`, nothing. Compute HMAC-SHA256 of the message with your API secret as the key, hex-encode the result and send it in `X-RAMPNOW-SIGN`. Your timestamp must be no more than 1 minute old when the request reaches Rampnow. Keep your server clock synchronized with NTP. ### Test vector Use these fixed inputs to check your signing code before you call the API. If your code produces the same signature, it builds the message correctly. | Input | Value | | - | - | | Secret key | `test-secret-key` | | Timestamp | `1772080882` | | Method | `POST` | | Path | `/api/partner/v1/ext/ramp_order/quote` | | Body | `{"orderType":"buy","srcAmount":"100","srcCurrency":"EUR","srcChain":"fiat","dstCurrency":"ETH","dstChain":"ethereum"}` | Message to sign (timestamp + method + path + body, no separators): ```text theme={null} 1772080882POST/api/partner/v1/ext/ramp_order/quote{"orderType":"buy","srcAmount":"100","srcCurrency":"EUR","srcChain":"fiat","dstCurrency":"ETH","dstChain":"ethereum"} ``` Expected `X-RAMPNOW-SIGN`: ```text theme={null} 369afe98d71b99e4121f88643b38956d6bc8c23376426fb2c80b7ba2e6961243 ``` The body is signed byte for byte. A space or a different key order gives a different signature, so always sign the exact string you send. ## Code examples Each example requests a quote. To check your code against the test vector, replace the secret with `test-secret-key`, the timestamp with `1772080882` and the body with the test vector body: the signature must match. ```bash cURL (bash) theme={null} API_KEY='your-api-key' API_SECRET='your-api-secret' BASE_URL='https://api.sandbox.rampnow.io' # POST: the body is part of the signature REQ_PATH='/api/partner/v1/ext/ramp_order/quote' BODY='{"orderType":"buy","srcAmount":"100","srcCurrency":"EUR","srcChain":"fiat","dstCurrency":"ETH","dstChain":"ethereum"}' TS=$(date +%s) SIGN=$(printf '%s' "${TS}POST${REQ_PATH}${BODY}" \ | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}') curl --request POST "${BASE_URL}${REQ_PATH}" \ --header 'Content-Type: application/json' \ --header "X-RAMPNOW-API-KEY: ${API_KEY}" \ --header "X-RAMPNOW-TIMESTAMP: ${TS}" \ --header "X-RAMPNOW-SIGN: ${SIGN}" \ --data "$BODY" # GET: no body in the signature; the query string is included as sent REQ_PATH='/api/partner/v1/ext/ramp_order/your-order-1001' TS=$(date +%s) SIGN=$(printf '%s' "${TS}GET${REQ_PATH}" \ | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}') curl --request GET "${BASE_URL}${REQ_PATH}" \ --header "X-RAMPNOW-API-KEY: ${API_KEY}" \ --header "X-RAMPNOW-TIMESTAMP: ${TS}" \ --header "X-RAMPNOW-SIGN: ${SIGN}" ``` ```python Python theme={null} import hashlib import hmac import json import time import requests API_KEY = "your-api-key" API_SECRET = "your-api-secret" BASE_URL = "https://api.sandbox.rampnow.io" PATH = "/api/partner/v1/ext/ramp_order/quote" body = json.dumps( { "orderType": "buy", "srcAmount": "100", "srcCurrency": "EUR", "srcChain": "fiat", "dstCurrency": "ETH", "dstChain": "ethereum", }, separators=(",", ":"), ) timestamp = str(int(time.time())) message = timestamp + "POST" + PATH + body signature = hmac.new(API_SECRET.encode(), message.encode(), hashlib.sha256).hexdigest() response = requests.post( BASE_URL + PATH, headers={ "Content-Type": "application/json", "X-RAMPNOW-API-KEY": API_KEY, "X-RAMPNOW-TIMESTAMP": timestamp, "X-RAMPNOW-SIGN": signature, }, data=body, # send the exact string you signed, not json= ) print(response.status_code, response.text) ``` ```javascript Node.js theme={null} const crypto = require("crypto"); const API_KEY = "your-api-key"; const API_SECRET = "your-api-secret"; const BASE_URL = "https://api.sandbox.rampnow.io"; const PATH = "/api/partner/v1/ext/ramp_order/quote"; const body = JSON.stringify({ orderType: "buy", srcAmount: "100", srcCurrency: "EUR", srcChain: "fiat", dstCurrency: "ETH", dstChain: "ethereum", }); const timestamp = String(Math.floor(Date.now() / 1000)); // seconds, not milliseconds const signature = crypto .createHmac("sha256", API_SECRET) .update(timestamp + "POST" + PATH + body) .digest("hex"); fetch(BASE_URL + PATH, { method: "POST", headers: { "Content-Type": "application/json", "X-RAMPNOW-API-KEY": API_KEY, "X-RAMPNOW-TIMESTAMP": timestamp, "X-RAMPNOW-SIGN": signature, }, body, // send the exact string you signed }) .then(async (res) => console.log(res.status, await res.text())) .catch(console.error); ``` ```go Go theme={null} package main import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io" "net/http" "strconv" "time" ) func main() { apiKey := "your-api-key" apiSecret := "your-api-secret" baseURL := "https://api.sandbox.rampnow.io" path := "/api/partner/v1/ext/ramp_order/quote" body := `{"orderType":"buy","srcAmount":"100","srcCurrency":"EUR","srcChain":"fiat","dstCurrency":"ETH","dstChain":"ethereum"}` timestamp := strconv.FormatInt(time.Now().Unix(), 10) mac := hmac.New(sha256.New, []byte(apiSecret)) mac.Write([]byte(timestamp + "POST" + path + body)) signature := hex.EncodeToString(mac.Sum(nil)) req, err := http.NewRequest(http.MethodPost, baseURL+path, bytes.NewBufferString(body)) if err != nil { panic(err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-RAMPNOW-API-KEY", apiKey) req.Header.Set("X-RAMPNOW-TIMESTAMP", timestamp) req.Header.Set("X-RAMPNOW-SIGN", signature) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(respBody)) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.HexFormat; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class RampnowApi { public static void main(String[] args) throws Exception { String apiKey = "your-api-key"; String apiSecret = "your-api-secret"; String baseUrl = "https://api.sandbox.rampnow.io"; String path = "/api/partner/v1/ext/ramp_order/quote"; String body = "{\"orderType\":\"buy\",\"srcAmount\":\"100\",\"srcCurrency\":\"EUR\"," + "\"srcChain\":\"fiat\",\"dstCurrency\":\"ETH\",\"dstChain\":\"ethereum\"}"; String timestamp = String.valueOf(Instant.now().getEpochSecond()); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] digest = mac.doFinal((timestamp + "POST" + path + body).getBytes(StandardCharsets.UTF_8)); String signature = HexFormat.of().formatHex(digest); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + path)) .header("Content-Type", "application/json") .header("X-RAMPNOW-API-KEY", apiKey) .header("X-RAMPNOW-TIMESTAMP", timestamp) .header("X-RAMPNOW-SIGN", signature) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); } } ``` ## Troubleshooting Every authentication failure returns HTTP `401` with `code` `100002` and `message` `not_authorized_err`, without a `displayError`. The response doesn't say which check failed, so work through this table: | Symptom | Likely cause | Fix | | - | - | - | | Every request fails | Sandbox key against production hosts, or the reverse | Match the key to the [environment](#environments) | | Every request fails | API key copied wrong, or the key was regenerated | Copy the current key and secret from the API Hub | | Every request fails | `X-RAMPNOW-TIMESTAMP` missing or not an integer | Send whole Unix seconds as a string, for example `1772080882` | | Fails intermittently or after a delay | Timestamp more than 1 minute old: clock drift, or a request built and sent later | Sync your clock with NTP; generate the timestamp just before sending | | Your signature doesn't match the [test vector](#test-vector) | Wrong secret, or message parts in the wrong order | Message is `timestamp + METHOD + path + body`, no separators | | Works for `GET` without a query, fails with one | Query string signed differently from how it was sent (re-encoded or re-ordered) | Sign the query exactly as it appears in the request URL | | Fails for every path | Path signed without the `/api/partner/v1` prefix, or with the host | Sign the path only, starting with `/api/partner/v1` | | `POST` fails, `GET` works | Signed body differs from the bytes sent: the HTTP client re-serialized the JSON or added whitespace | Serialize once and send that exact string (for example `data=` in Python, not `json=`) | | `GET` fails, `POST` works | A body (even `{}`) was included in the signature for a `GET` | Sign no body for `GET` | Uppercase and lowercase hex signatures are both accepted. ## Quick reference | Component | Rule | | - | - | | Secret | Your API secret from the API Hub | | Timestamp | Unix seconds; no more than 1 minute old | | Method | Uppercase (`GET`, `POST`) | | Path | Starts with `/api/partner/v1`; includes the query string exactly as sent | | Body | `POST`/`PUT`/`PATCH`: exact bytes sent. `GET`: nothing | | Signature | HMAC-SHA256, hex-encoded | # Get asset config Source: https://docs.rampnow.io/api-reference/configuration/get-asset-config /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/asset_config # Get blocked currencies Source: https://docs.rampnow.io/api-reference/configuration/get-blocked-currencies /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/blocked_currencies # Get chain config Source: https://docs.rampnow.io/api-reference/configuration/get-chain-config /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/chain_config # Get order config Source: https://docs.rampnow.io/api-reference/configuration/get-order-config /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/config # Get payment mode config Source: https://docs.rampnow.io/api-reference/configuration/get-payment-mode-config /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/payment_mode_config # Get token config Source: https://docs.rampnow.io/api-reference/configuration/get-token-config /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/token_config # Error Codes Source: https://docs.rampnow.io/api-reference/error-codes HTTP statuses, application error codes, and which errors to retry ## Response envelope Every response, successful or not, uses the same envelope: ```json theme={null} { "code": 100004, "message": "Amount is below the minimum for this route.", "displayError": "Amount is below the minimum for this route.", "traceId": "b1f2c3d4e5f60718293a4b5c6d7e8f90" } ``` | Field | Description | | - | - | | `code` | Application code. `0` means success. **Branch your logic on this**, not on the HTTP status or the message text | | `message` | `success` on success. On error: the `displayError` text when there is one, otherwise the code's label from the table below (for example `invalid_args_err`). Never contains internal or provider error details | | `displayError` | Optional message that is safe to show your end user. When absent, show your own copy keyed on `code` | | `traceId` | Request id in Rampnow's logs, also returned in the `X-Request-ID` header. Include it when you contact support | | `data` | The response body on success | Don't parse `message` for meaning: its text can change. Use `code` in your logic and `displayError` (or your own copy) for user-facing text. ## HTTP statuses | HTTP status | When | | - | - | | `200` | Success, **and** some failures (see below) | | `400` | Invalid arguments (`100004`, `100015`) | | `401` | Authentication or authorisation failed (`100001`, `100002`) | | `404` | Resource not found (`100000`) | | `409` | Conflict (`100005`) | | `415` | The `Accept` header doesn't allow JSON. Plain-text body, no envelope | | `500` | Every other error code, including business errors such as an invalid quote | | `501` | Not implemented (`100011`) | **Check `code` even when the HTTP status is `200`.** Some failures currently return `200`: * A malformed JSON body, or one that fails field validation, returns `200` with `code` `100008` (`internal_err`). Fix the request; don't retry it unchanged. * A [quote](/api-reference/onramp-&-offramp-intent/get-a-quote) for an unsupported pair, or an amount outside the route's limits, returns `200` with `code` `0`, empty quote data and a `message` such as `No routes found for the selected options`. Rampnow doesn't currently apply API rate limits and doesn't send `Retry-After`. ## Error codes | Code | Label | HTTP | Category | Retry? | Typical cause on partner endpoints | | - | - | - | - | - | - | | `0` | `success` | 200 | Success | — | Request succeeded | | `100000` | `not_found_err` | 404 | Client | No | Resource doesn't exist, or no partner wallet is configured for an order without `wallet` | | `100001` | `not_authenticated_err` | 401 | Auth | No | Missing credentials | | `100002` | `not_authorized_err` | 401 | Auth | No | Any [authentication failure](/api-reference/authentication#troubleshooting): bad key, signature or timestamp | | `100003` | `unknown_err` | 500 | Server | Yes | Unexpected server error | | `100004` | `invalid_args_err` | 400 | Client | No | A field is missing or invalid: wallet address, amount below the route minimum, checkout not enabled for your account | | `100005` | `conflict_err` | 409 | Client | No | The wallet address already belongs to another customer | | `100006` | `db_err` | 500 | Server | Yes | Temporary storage error | | `100007` | `marshal_err` | 500 | Server | Yes | Temporary serialization error | | `100008` | `internal_err` | 500 or 200 | Server / Client | 500: Yes. 200: No | `500`: internal error. `200`: malformed or invalid request body | | `100009` | `http_err` | 500 | Upstream | Yes | A payment or liquidity provider call failed | | `100010` | `setup_err` | 500 | Server | No | Configuration problem on Rampnow's side. Contact support | | `100011` | `not_implemented_err` | 501 | Client | No | Feature not available | | `100012` | `ext_api_err` | 500 | Upstream | Yes | A provider returned an error | | `100013` | `timeout_err` | 500 | Upstream | Yes | A provider call timed out | | `100014` | `expired_err` | 500 | Business | No | The order expired. Create a new order | | `100015` | `invalid_err` | 400 | Client | No | Invalid data in the request | | `100017` | `request_err` | 500 | Upstream | No | A provider rejected the request. Contact support with the `traceId` | | `100018` | `blocked_err` | 500 | Business | No | Your partner account or the wallet is inactive or blocked | | `100019` | `blacklisted_err` | 500 | Business | No | The user, wallet or entity is blacklisted | | `100020` | `retry_exceeded_err` | 500 | Business | No | Too many attempts for this operation | | `100021` | `not_supported_err` | 500 | Business | No | Unsupported option, for example an `orderType` other than `buy` or `sell` | | `100023` | `invalid_quote_err` | 500 | Business | No | The amount can't be quoted: outside the route limits, or both or neither of `srcAmount`/`dstAmount` set | | `100024` | `server_err` | 500 | Upstream | Yes | A provider had a server error | | `100025` | `request_timeout_err` | 500 | Upstream | Yes | A provider request timed out | | `100026` | `payload_size_err` | 500 | Upstream | No | A provider rejected the payload size | | `100027` | `rate_limit_err` | 500 | Upstream | Yes | A provider rate-limited Rampnow. Retry with a longer backoff | | `100028` | `bad_gateway_err` | 500 | Upstream | Yes | A provider gateway error | | `100029` | `service_unavailable_err` | 500 | Upstream | Yes | A provider is temporarily unavailable | | `100030` | `gateway_timeout_err` | 500 | Upstream | Yes | A provider gateway timed out | | `100031` | `invalid_method_err` | 500 | Upstream | No | A provider rejected the request method. Contact support | | `100032` | `unprocessable_entity_err` | 500 | Upstream | No | A provider rejected the request content | ## Retrying * Retry only codes marked **Yes**, with exponential backoff (for example 1s, 2s, 4s, up to 5 attempts). * Never retry `4xx` responses or business errors unchanged. Fix the request or ask the user to change it. * Quotes, configuration endpoints and Get Order are safe to retry. **Don't blindly retry Create Order (`/ext/ramp_order/checkout`).** Orders aren't de-duplicated by `externalOrderUid`, so a retry after a timeout or `5xx` can create a second order. First call [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id) with your `externalOrderUid`, and retry only if `orderList` is empty. # Go-Live Checklist Source: https://docs.rampnow.io/api-reference/go-live-checklist What to check before you switch to production Go through this list before you ask for production access. Production access is only granted after your integration passes in [sandbox](/api-reference/sandbox-testing). ## Production access You have tested every outcome in [Sandbox Testing](/api-reference/sandbox-testing): completed, payment failed and expired. You have asked [support@rampnow.io](mailto:support@rampnow.io) for production access and have your production API key and secret from the [Partner Dashboard](https://partner.rampnow.io). Production uses `https://api.rampnow.io` and `https://app.rampnow.io`, with production keys. No sandbox host or key is left in your production config. ## Requests Your API secret is only on your server. It never ships in a browser or mobile app. Your server clock is synced (NTP). The signature timestamp must be within 1 minute of Rampnow's time. See [Authentication](/api-reference/authentication). Your error handling branches on the error `code`, not on `message`. You show `displayError` to users when it is present. See [Error Codes](/api-reference/error-codes). ## Order ids Every order gets its own unique `externalOrderUid`, and you store it before you call Create Order. Before you retry a Create Order call that timed out or failed, look the id up with [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id). Rampnow does not reject a repeated `externalOrderUid`, so a blind retry can create a second order. ## Webhooks Your webhook endpoint is set in the API Hub of the Partner Dashboard, subscribed to `ramp_order.status_change`, and reachable over HTTPS. You verify the `X-RAMPNOW-SIGN` signature on every webhook and reject requests that fail. You return any `2xx` fast and do slow work afterwards. Failed deliveries are retried for about 24 hours. You deduplicate on `eventUid`, so a retried event is only processed once. See the [Webhook Guide](/api-reference/webhook). ## Status handling You handle every status on [Order Status](/api-reference/order-status), and unknown values don't break your code. You treat `failed` as in progress, not final. Rampnow operations either retry the order, so it can still reach `completed`, or block and refund it. You treat `completed`, `payment_failed`, `expired`, `canceled` and `blocked` as final for the user, but still apply later webhooks: a late payment can move a `payment_failed` or `expired` order to `processing`. You reconcile on `dstFillAmount` (the amount actually delivered), not on the quoted `dstAmount`. ## Return URL You set `returnUrl` on each order, with your own order id in it, so the user lands back on the right page in your app after checkout. You don't treat the return to your app as proof of payment. A user can come back before paying or after a failure. Read the status from the webhook or Get Order. # Create an intent order Source: https://docs.rampnow.io/api-reference/onramp-&-offramp-intent/create-an-intent-order /api-reference/partner_external.yaml post /api/partner/v1/ext/ramp_order/checkout # Get a quote Source: https://docs.rampnow.io/api-reference/onramp-&-offramp-intent/get-a-quote /api-reference/partner_external.yaml post /api/partner/v1/ext/ramp_order/quote # Get an order by external ID Source: https://docs.rampnow.io/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id /api-reference/partner_external.yaml get /api/partner/v1/ext/ramp_order/{uid} # Order Status Source: https://docs.rampnow.io/api-reference/order-status Every order and payment status, how an order moves between them, and what to do in each This page is the single reference for order statuses. Webhooks carry the status in `payload.orderStatus`. [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id) returns it in `status`. The values are the same. ## Lifecycle ```mermaid theme={null} stateDiagram-v2 [*] --> unknown: POST /ext/ramp_order/checkout unknown --> created: customer confirms on hosted page created --> payment_processing: payment started created --> processing: payment accepted payment_processing --> processing: payment accepted created --> payment_failed: payment declined payment_processing --> payment_failed: payment declined created --> expired: not paid in time payment_processing --> expired: payment expired processing --> completed: delivered / paid out processing --> failed: delivery / payout failed failed --> processing: retried by Rampnow operations failed --> blocked: compliance block payment_failed --> processing: late payment accepted expired --> processing: late payment accepted completed --> [*] blocked --> [*] ``` ## Order statuses | Status | Meaning | Webhook | Final | What you should do | | - | - | - | - | - | | `unknown` | Order created with `/checkout`, but the customer hasn't confirmed it on the hosted page yet. It has no time limit in this state. | No | No | Nothing. The order is visible only by polling. Wait for `created`. | | `created` | The customer confirmed the order. Rampnow is waiting for the fiat payment (buy) or the crypto deposit (sell). Unpaid orders expire after 15 minutes. | Yes | No | Show "waiting for payment". | | `payment_processing` | The payment is clearing (buy), or the deposit address is issued and the deposit is being detected and confirmed (sell). | No | No | Nothing. Only visible by polling. | | `processing` | Payment accepted. Buy: crypto is being sent to the destination wallet. Sell: the deposit is converted and the fiat payout is in progress. | Yes | No | Show "in progress". | | `completed` | Crypto delivered (buy) or fiat paid out (sell). | Yes | Yes | Mark the order successful. Reconcile on `dstFillAmount`, not `dstAmount`. | | `failed` | Delivery (buy) or payout (sell) failed. The Rampnow operations team investigates and either retries the order, which moves it back to `processing`, or blocks and refunds it. | Yes | No | Don't close or refund on your side. Show "delayed" and keep listening. | | `payment_failed` | The card or bank payment was declined, failed or canceled (buy), or the deposit didn't match the order (sell). | Yes | Yes\* | Tell the user the payment failed. A retry needs a new order. | | `expired` | The order wasn't paid within its window. | Yes | Yes\* | Tell the user the order expired. A retry needs a new order. | | `blocked` | Stopped by compliance screening. Funds are refunded where applicable, and `paymentStatus` becomes `refunded`. | Yes | Yes | Mark the order failed. Don't retry the same order. | | `canceled` | Reserved. Not currently emitted for ramp orders. | — | Yes | Handle as final if you receive it. | | `partially_failed` | Reserved. Not currently emitted for ramp orders. | — | Yes | Handle as final if you receive it. | \* `payment_failed` and `expired` are final for your user experience, but a payment that is accepted late (for example a bank transfer that arrives after the window) moves the order to `processing`. Keep handling webhooks for these orders. New statuses may be added. Treat any value you don't recognise as "in progress" and log it, rather than failing. ## Payment statuses `paymentStatus` tracks the payment leg: the fiat payment for a buy, the crypto deposit for a sell. It drives the order status as shown below. A webhook is only sent when the **order** status changes, so payment-only changes don't produce a webhook. | Payment status | Meaning | Order status | | - | - | - | | `unknown` | No payment yet | unchanged | | `created` | Payment created with the payment provider | `payment_processing` | | `pending` | Waiting for the customer to pay. On a sell, the deposit address is issued | `payment_processing` | | `authorized` | Payment authorised. On a sell, the deposit is seen on-chain but not yet confirmed | `payment_processing` | | `received` | Funds received. On a sell, the deposit is confirmed and being converted | `payment_processing` | | `review` | Payment held for manual review (for example a payer name mismatch) | `payment_processing` | | `processing_failed` | Conversion of the received funds failed and is being retried | `payment_processing` | | `accepted` | Payment final and accepted | `processing` | | `rejected` | Payment rejected. On a sell, the deposit amount didn't match | `payment_failed` | | `failed` | Payment failed | `payment_failed` | | `canceled` | Payment canceled | `payment_failed` | | `expired` | Payment not completed in time | `expired` | | `refunded` | Payment refunded to the customer | unchanged | | `chargebacked` | Card payment charged back | unchanged | ## Buy vs sell | | Buy (onramp) | Sell (offramp) | | - | - | - | | Payment leg | Fiat payment by card or bank | Crypto deposit to the address shown on the hosted page | | Payment window | 15 minutes after the customer confirms, unless a payment has started. Pending bank transfers can stay open up to 7 days | The deposit address is valid for 25 minutes after it is issued | | `processing` means | Payment accepted, crypto being delivered | Deposit converted, fiat payout in progress | | `completed` means | Crypto delivered to the destination wallet | Fiat paid out to the bank account | | `dstFillAmount` | Crypto amount delivered on-chain | Fiat amount paid out | | `transactionHash` | On-chain transaction hash(es) of the delivery | Payout provider's reference (for example a bank trace number), not the deposit hash | | `walletAddress` | Destination crypto address | Destination IBAN, or the account number when there is no IBAN | ## Handling rules * **Status is the current state, not a transition.** Each webhook carries the order's status at the time it was sent. Two quick changes can produce two events with the same status. Store the latest status per order and ignore events whose `payload.updateTime` is older than the one you have. * **De-duplicate webhooks on `eventUid`.** Retries of the same event reuse it. See [Webhooks](/api-reference/webhook). * **Don't treat the `returnUrl` redirect as proof of success.** Confirm the status from a webhook or by [polling the order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id). * **Field names differ by source.** Webhooks use `orderStatus`. Get Order uses `status`. # Overview Source: https://docs.rampnow.io/api-reference/overview Basics of Rampnow API ## Getting Started Begin your integration using the Sandbox environment at `api.sandbox.rampnow.io`. This allows you to test without affecting real data. Verify all API endpoints, error handling and edge cases work as expected in Sandbox. Once testing is complete and your integration is stable, request Production access. Switch your base URL to `api.rampnow.io` and go live with confidence. ## Open API documentation Our OpenAPI documentation: [OpenAPI](https://rampnow-io.github.io/core/partner_external.yaml). For the starter kit, we are using the following OpenAPI specification. View the OpenAPI specification file ## API Environments Rampnow provides two separate environments to support your development and production workflows:
`https://api.rampnow.io` Live environment for production applications with real transactions. `https://api.sandbox.rampnow.io` Testing environment for development and integration testing.
### Environment Comparison | Feature | Sandbox | Production | | - | - | - | | Base URL | api.sandbox.rampnow\.io | api.rampnow\.io | | Purpose | Testing & Development | Live Transactions | | Data | Test data only | Real data | | Access | Immediate | Requires approval | | Transactions | Simulated | Real | Both environments use the same API structure and authentication methods. Simply change the base URL when moving from Sandbox to Production. You must thoroughly test your integration in Sandbox mode before requesting access to Production. Production access is granted only after successful integration testing. ## API Versioning The Rampnow API follows a stable versioning policy to ensure your integrations remain functional. ### Our Versioning Promise **Breaking Changes** * All incompatible or breaking changes are versioned * Existing endpoints will continue to work without modification * You control when to migrate to new API versions **Non-Breaking Changes** * New fields may be added to JSON responses at any time * New optional parameters may be introduced * New endpoints may be added **Important:** We may add additional fields to JSON responses without notice or version changes. Ensure your code can handle unknown properties gracefully. ## Response Format All API responses return JSON payloads with a consistent structure: ### Success Response Every response is wrapped in `{ code, message, traceId, data }` (`data` holds the endpoint's payload; `code` `0` = success). ```JSON JSON theme={null} { "code": 0, "message": "success", "traceId": "b1f2c3d4-...", "data": { "orderUid": "5de57551-3272-4af5-86a4-5eeae6ccc37f", "status": "completed" } } ``` ### Error Response ```JSON JSON theme={null} { "code": 100004, "message": "invalid_args_err", "displayError": "Amount is below the minimum for this route.", "traceId": "b1f2c3d4-..." } ``` Rely on **`code`** to branch your logic, not on the message text. * **`message`** is a stable, generic label for the error `code` (e.g. `invalid_args_err`). It never contains internal or upstream-provider error detail. * **`displayError`** is an optional, human-readable message safe to show to your end user (e.g. "Amount is below the minimum for this route."). It is present only when a user-facing message applies; fall back to your own copy keyed on `code` when it is absent. * **`traceId`** identifies the request in our logs — include it when contacting support. ## Key Features Built on REST principles with predictable resource-oriented URLs and standard HTTP methods. All responses are returned in JSON format for easy parsing and integration. All API requests must be made over HTTPS. Plain HTTP requests are rejected for security. Secure request signing using HMAC-SHA256 ensures request authenticity and integrity. Detailed error codes and messages help you quickly identify and resolve issues. ## Next Steps Ready to start building? Here's what to do next:
Learn how to authenticate your API requests Understand error codes and how to handle them Explore available API endpoints
# Payment Simulator Source: https://docs.rampnow.io/api-reference/payment-simulator Choose the outcome of every sandbox payment from your Partner Dashboard In sandbox, payments go through a payment simulator instead of real card networks and banks. You decide whether each payment is accepted, declined or expired, so you can test every order outcome without moving real money. The simulator only runs in sandbox. Production payments always go to the real payment provider. ## Enable simulation You configure payment simulation yourself from the [Partner Dashboard](https://partner.rampnow.io) on your sandbox account. 1. Sign in to the sandbox Partner Dashboard. 2. Open your account settings and turn on **Payment simulation**. Once it's on, every payment on your sandbox account uses the simulator. ## Card and other hosted payments After the user is redirected to checkout and picks a payment method, the sandbox shows a simulator page in place of the real payment provider. Pick the outcome you want to test from the status dropdown. ## Bank transfers For push payments the user sends themselves (SEPA, ACH, wire, PIX, UPI, SPEI and similar), the user cannot finish the payment on the checkout page. You settle it yourself from the Partner Dashboard: 1. Open the order under **Ramp Orders**. 2. Choose **Settle Transfer**. 3. Pick an outcome: Accepted, Rejected, Failed, Canceled or Expired. The action is only shown while the payment is still pending. ## Magic amounts If a payment is settled without an outcome chosen, the simulator reads the outcome from the payment amount: | Amount | Outcome | | - | - | | `9999.01` | Declined: insufficient funds | | `9999.02` | Declined: fraud | | `9999.03` | Declined: 3DS required | | `9999.90` | Provider timeout | | `9999.91` | Soft decline | | Any other amount | Accepted | An outcome you pick on the simulator page or in the Partner Dashboard always takes priority over the amount. ## What each outcome does to the order | Payment outcome | Order status | Webhook | | - | - | - | | Accepted | `processing`, then `completed` once crypto is delivered | Yes | | Rejected, Failed or Canceled | `payment_failed` (final) | Yes | | Expired | `expired` (final) | Yes | Orders that are not paid within 15 minutes of the user confirming them also expire on their own. The full list of statuses is on [Order Status](/api-reference/order-status). # Postman Collection Source: https://docs.rampnow.io/api-reference/postman-collection Try the Rampnow partner API from Postman with automatic request signing The Rampnow Postman collection contains every partner API endpoint, with a pre-request script that signs each request for you. Use it to try the API before you write any code. Collection JSON, generated from the same OpenAPI spec as this API reference. ## Prerequisites * A Rampnow Partner Dashboard account, with your API key and secret from the API Hub * The [Postman app](https://www.postman.com/downloads/) or Postman on the web ## Set up the collection In Postman, choose **Import**, paste this link and confirm: ```text theme={null} https://rampnow-io.github.io/core/partner_external-postman.json ``` Open the imported collection, go to the **Variables** tab and add two variables. They must be **collection** variables: the signing script doesn't read environment variables. | Variable | Value | | - | - | | `apiKey` | Your API key | | `apiSecret` | Your API secret | Mark `apiSecret` as secret, and don't share or export the collection with it filled in. The collection's `baseUrl` variable defaults to production, `https://api.rampnow.io`. To use the sandbox, set it to `https://api.sandbox.rampnow.io` and use your sandbox key and secret. Keys only work on their own environment. Start with **Onramp & Offramp Intent → Get a quote**, edit the body and choose **Send**. ## What's in the collection Requests are grouped into folders by API section: | Folder | Requests | | - | - | | **Onramp & Offramp Intent** | Get a quote, Create a checkout order, Get an order by external ID | | **Configuration** | Get order config, Get asset config, Get chain config, Get token config, Get payment mode config, Get blocked currencies | Each request uses the full path, for example `{{baseUrl}}/api/partner/v1/ext/ramp_order/quote`, so `baseUrl` is the host only. ## How request signing works The collection runs this pre-request script before every request: ```javascript theme={null} var apiKey = pm.collectionVariables.get("apiKey");; var apiSecret = pm.collectionVariables.get("apiSecret"); var body = ""; if (pm.request.body.raw) { body = pm.request.body.raw; } const timestamp = Date.now().toString(); let payload = [timestamp, pm.request.method, pm.request.url.getPathWithQuery(), body].join("") let signature = CryptoJS.enc.Hex.stringify(CryptoJS.HmacSHA256(payload, apiSecret)); pm.request.headers.add({ key: "X-RAMPNOW-API-KEY", value: apiKey }); pm.request.headers.add({ key: "X-RAMPNOW-TIMESTAMP", value: timestamp }); pm.request.headers.add({ key: "X-RAMPNOW-SIGN", value: signature }); ``` It follows the [signing rules](/api-reference/authentication#sign-a-request): 1. Reads `apiKey` and `apiSecret` from the collection variables. 2. Takes the raw request body, or an empty string if there is none. 3. Builds the message `timestamp + method + path with query + body` and signs it with HMAC-SHA256 using your secret. 4. Adds the `X-RAMPNOW-API-KEY`, `X-RAMPNOW-TIMESTAMP` and `X-RAMPNOW-SIGN` headers. Because the body is signed exactly as Postman sends it, you can edit request bodies freely. Two differences from the rules your own code should follow: * The script uses `Date.now()`, a timestamp in **milliseconds**, which the API currently accepts. Your own code must send **seconds**, as described in [Authentication](/api-reference/authentication). * The script signs the body on any method. Leave the body of `GET` requests empty, or the signature won't match. ## Troubleshooting * **`401` with `code` `100002`:** check that `apiKey` and `apiSecret` are set as collection variables (not environment variables), and that the key matches the `baseUrl` environment. See [Authentication → Troubleshooting](/api-reference/authentication#troubleshooting). * **`200` with a non-zero `code`:** the request reached the API but failed. Look the code up in [Error Codes](/api-reference/error-codes). ## Next steps * [Authentication](/api-reference/authentication): sign requests from your own code * [API overview](/api-reference/overview): endpoints and integration options * [Error Codes](/api-reference/error-codes): handle failures and retries # Sandbox Testing Source: https://docs.rampnow.io/api-reference/sandbox-testing Test payments, outcomes and testnet deliveries before going live The sandbox runs the same API as production, but no real money or mainnet crypto moves. Use it to run every order outcome your integration must handle before you ask for production access. ## Hosts and keys | | Sandbox | | - | - | | API base URL | `https://api.sandbox.rampnow.io` | | Widget and checkout host | `https://app.sandbox.rampnow.io` | Sandbox keys only work on sandbox hosts. See [Environments](/api-reference/authentication#environments). ## Testnets and test tokens In sandbox, crypto is delivered on testnets. Testnet chains come back from the supported-config endpoints with `isTestnet: true`. Production only returns mainnet chains. | Chain code | Assets | | - | - | | `sepolia` | `ETH`, `USDC`, `USDT` | | `base-sepolia` | `ETH`, `USDC` | | `solana-devnet` | `SOL`, `USDC` | | `arbitrum-sepolia` | `USDC` | | `optimism-sepolia` | `USDC` | | `polygon-amoy` | `POL`, `USDC` | Use these codes as `dstChain` (onramp) or `srcChain` (offramp). Your account may have only some of them enabled, so check the live list first: see [Supported config](/quickstart/ramp-intent/supported-config). Use a testnet wallet address you control. ## Simulated payments In sandbox, payments go through a payment simulator instead of real card networks and banks. You turn simulation on and pick the outcome of each payment (accepted, declined or expired) from your Partner Dashboard. Enable simulation, settle card and bank transfer payments, and see what each outcome does to the order. ## KYC Sandbox has no self-serve way to force a KYC approval or decline yet. To test KYC outcomes, email [support@rampnow.io](mailto:support@rampnow.io). ## Suggested test run Create an onramp order to a testnet wallet, accept the payment in the [simulator](/api-reference/payment-simulator), and confirm you get `processing` and `completed` webhooks and a `dstFillAmount`. Reject or fail the payment. Confirm you show the user an error and treat `payment_failed` as final. Expire the payment, or leave the order unpaid for 15 minutes. Confirm you handle `expired`. Verify webhook signatures, and check that [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id) returns the same status. When every case passes, move on to the [Go-Live Checklist](/api-reference/go-live-checklist). # Webhook Configuration Source: https://docs.rampnow.io/api-reference/webhook Receive real-time notifications for order status changes Webhooks are optional but recommended. They are the fastest way to learn that an order changed status. ## Overview Rampnow sends a `POST` request to your webhook endpoint each time an order's status changes. Configure the endpoint in your Rampnow Partner Dashboard. ## Configuration steps Navigate to the **API Hub** in your [Rampnow Partner Dashboard](https://partner.rampnow.io/api/auth/signin?callbackUrl=%2F). Enter the HTTPS endpoint on your server that will receive webhook notifications. All webhooks are delivered to this account-level URL. The `webhookUrl` field on [Create Order](/quickstart/ramp-intent/create-order) is not currently used as a delivery target, so don't rely on it. Subscribe to `ramp_order.status_change`. Rampnow only sends events you are subscribed to. *** ## Order events Every event has `eventType: "ramp_order.status_change"`. The order's status is in `payload.orderStatus`, not in the event name. A webhook is sent when the **order** status changes. These do not produce a webhook: * `unknown` (the order was created but the customer hasn't confirmed it yet) * `payment_processing` * changes to `paymentStatus` alone The meaning of every status, which ones are final and what to do in each is on [Order Status](/api-reference/order-status). **`failed` is not final.** Don't close or refund the order on your side when you receive `failed`. The Rampnow operations team either retries it (it moves back to `processing`) or blocks and refunds it. Wait for the next webhook. *** ## Delivery and retries * **Success** is any `2xx` response. The response body, content type and length are ignored, so an empty body is fine. * **Failure** is any non-2xx status or a transport error (connection refused, DNS, TLS, timeout). * **Retries:** a failed event is retried up to **10 times** with exponential backoff (6 minutes, doubling, then every 6 hours), spanning roughly **24 hours** in total. * **No auto-disable:** repeated failures never disable your endpoint. After the retries run out, the event stays failed and you can re-send it from the Partner Dashboard. * Each attempt re-reads the order, so a retry after an outage still carries the final `dstFillAmount`, `transactionHash` and `walletAddress`. Return `2xx` as soon as you have verified and stored the event, then process it asynchronously. Slow handlers risk timeouts and duplicate deliveries. ### Idempotency De-duplicate on **`eventUid`**. It is generated once per status change and reused on every retry of that event. `orderUid` is not a de-duplication key: one order produces several events. Because each event carries the order's status at the time it was sent, two quick changes can arrive as two events with the same status, and events can arrive out of order. Store the latest status per order and ignore any event whose `payload.updateTime` is older than the one you already have. *** ## Signature verification Every webhook is signed so you can verify it came from Rampnow. Reject any request whose signature does not match. | Header | Description | | - | - | | `X-RAMPNOW-SIGN` | Hex-encoded HMAC-SHA256 signature (lowercase) | | `X-RAMPNOW-TIMESTAMP` | Unix timestamp in **seconds** when this attempt was sent | **Signing secret:** your API secret, the same secret you use to sign API requests (from the API Hub in the Partner Dashboard). There is no separate webhook secret. **Signed message:** the concatenation, in this exact order, with no separators: ```text theme={null} message = timestamp + "POST" + path + rawBody ``` * `timestamp`: the value of the `X-RAMPNOW-TIMESTAMP` header. * `path`: the path, plus the query string if any, of the **webhook URL configured in API Hub**, for example `/rampnow/webhook`. Use the configured value as a constant. Don't read it from the incoming request: a proxy or load balancer may rewrite the path. * `rawBody`: the exact bytes of the request body, before any JSON parsing. ### Test vector Use these fixed inputs to check your verification code before you go live. If your code computes the same signature, it builds the message correctly. The body is the [Buy · created](#payload-examples) example below, exactly as Rampnow sends it on the wire: compact JSON, with no spaces or line breaks. | Input | Value | | - | - | | API secret | `test-secret-key` | | `X-RAMPNOW-TIMESTAMP` | `1772080882` | | Webhook path | `/rampnow/webhook` | | Body | The single line below | Body: ```json theme={null} {"createdAt":"2026-10-01T12:00:02Z","eventUid":"853d470c-db91-4c6e-9724-acfccd138306","eventType":"ramp_order.status_change","payload":{"orderUid":"5de57551-3272-4af5-86a4-5eeae6ccc37f","createTime":"2026-10-01T11:58:40Z","updateTime":"2026-10-01T12:00:00Z","externalOrderUid":"your-order-1001","orderType":"buy","orderStatus":"created","paymentStatus":"created","paymentMode":"card","srcCurrency":"EUR","srcChain":"fiat","srcAmount":"250","dstCurrency":"ETH","dstChain":"ethereum","dstAmount":"0.0812","dstFillAmount":"0","walletAddress":"0x535ebf73d0E20db0A72509207599C816510e11C9","walletAddressTag":"","feeDetails":[{"type":"platform","fee":"4.5","currency":"EUR","isInclusive":true},{"type":"network","fee":"0.8","currency":"EUR","isInclusive":true},{"type":"partner","fee":"2.5","currency":"EUR","isInclusive":true}],"partnerFee":"2.71","partnerCurrency":"USDC","cryptoAccountDetail":{"custodyType":"unhosted","address":"0x535ebf73d0E20db0A72509207599C816510e11C9"}}} ``` Message to sign: `1772080882` + `POST` + `/rampnow/webhook` + the body above, with no separators. Expected `X-RAMPNOW-SIGN`: ```text theme={null} 79ae56d41cc58a6bf20b45405eec78115944831dcd2f37a061a30754b4aead9f ``` The test timestamp is in the past, so a full verification would reject it under the [replay window](#replay-window). Check the signature on its own with the `rampnowSignature` / `rampnow_signature` helper in the examples below. ### Replay window Rampnow signs every delivery attempt, including retries, with a fresh timestamp. A legitimate request is therefore never old. Reject any request whose timestamp is more than **5 minutes** (300 seconds) away from your server's clock, and keep your clock in sync with NTP. Together with `eventUid` de-duplication, this stops replayed requests. ### Examples ```javascript Node.js (Express) theme={null} const crypto = require("crypto"); const express = require("express"); const API_SECRET = process.env.RAMPNOW_API_SECRET; // Path (and query, if any) of the webhook URL configured in API Hub const WEBHOOK_PATH = "/rampnow/webhook"; const TOLERANCE_SECONDS = 300; // Test vector: with API_SECRET = "test-secret-key", // rampnowSignature("1772080882", Buffer.from()).toString("hex") // must return 79ae56d41cc58a6bf20b45405eec78115944831dcd2f37a061a30754b4aead9f function rampnowSignature(timestamp, rawBody) { return crypto .createHmac("sha256", API_SECRET) .update(timestamp + "POST" + WEBHOOK_PATH) .update(rawBody) .digest(); } function verifyRampnowWebhook(timestamp, signature, rawBody) { if (!/^\d+$/.test(timestamp)) return false; const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)); if (age > TOLERANCE_SECONDS) return false; signature = signature.toLowerCase(); if (!/^[0-9a-f]{64}$/.test(signature)) return false; // Both buffers are 32 bytes here, so timingSafeEqual cannot throw return crypto.timingSafeEqual( Buffer.from(signature, "hex"), rampnowSignature(timestamp, rawBody) ); } const app = express(); // express.raw keeps the body as a Buffer: don't use express.json() on this route app.post(WEBHOOK_PATH, express.raw({ type: "*/*" }), (req, res) => { const ok = verifyRampnowWebhook( req.get("X-RAMPNOW-TIMESTAMP") || "", req.get("X-RAMPNOW-SIGN") || "", req.body ); if (!ok) return res.sendStatus(401); const event = JSON.parse(req.body.toString("utf8")); // De-duplicate on event.eventUid, store it, then process asynchronously res.sendStatus(200); }); app.listen(3000); ``` ```python Python (Flask) theme={null} import hashlib import hmac import json import os import time from flask import Flask, abort, request API_SECRET = os.environ["RAMPNOW_API_SECRET"].encode() # Path (and query, if any) of the webhook URL configured in API Hub WEBHOOK_PATH = "/rampnow/webhook" TOLERANCE_SECONDS = 300 app = Flask(__name__) # Test vector: with API_SECRET = b"test-secret-key", # rampnow_signature("1772080882", ) # must return "79ae56d41cc58a6bf20b45405eec78115944831dcd2f37a061a30754b4aead9f" def rampnow_signature(timestamp: str, raw_body: bytes) -> str: message = timestamp.encode() + b"POST" + WEBHOOK_PATH.encode() + raw_body return hmac.new(API_SECRET, message, hashlib.sha256).hexdigest() def verify_rampnow_webhook(timestamp: str, signature: str, raw_body: bytes) -> bool: if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS: return False expected = rampnow_signature(timestamp, raw_body) return hmac.compare_digest(signature.lower().encode(), expected.encode()) @app.post(WEBHOOK_PATH) def rampnow_webhook(): raw_body = request.get_data() # raw bytes, read before any JSON parsing if not verify_rampnow_webhook( request.headers.get("X-RAMPNOW-TIMESTAMP", ""), request.headers.get("X-RAMPNOW-SIGN", ""), raw_body, ): abort(401) event = json.loads(raw_body) # De-duplicate on event["eventUid"], store it, then process asynchronously return "", 200 if __name__ == "__main__": app.run(port=3000) ``` Verify the **raw** request body. Frameworks that parse and re-serialize JSON can reorder keys or change whitespace, which changes the bytes and breaks the signature. *** ## Payload Each request body is an envelope with the order data in `payload`: | Field | Type | Description | | - | - | - | | `eventUid` | string | Unique event id, stable across retries. De-duplicate on this | | `eventType` | string | `ramp_order.status_change` | | `createdAt` | string | Event envelope timestamp. Use `payload.updateTime` for ordering instead | | `payload` | object | The order, described below | ### Payload examples ```json Buy · created theme={null} { "createdAt": "2026-10-01T12:00:02Z", "eventUid": "853d470c-db91-4c6e-9724-acfccd138306", "eventType": "ramp_order.status_change", "payload": { "orderUid": "5de57551-3272-4af5-86a4-5eeae6ccc37f", "createTime": "2026-10-01T11:58:40Z", "updateTime": "2026-10-01T12:00:00Z", "externalOrderUid": "your-order-1001", "orderType": "buy", "orderStatus": "created", "paymentStatus": "created", "paymentMode": "card", "srcCurrency": "EUR", "srcChain": "fiat", "srcAmount": "250", "dstCurrency": "ETH", "dstChain": "ethereum", "dstAmount": "0.0812", "dstFillAmount": "0", "walletAddress": "0x535ebf73d0E20db0A72509207599C816510e11C9", "walletAddressTag": "", "feeDetails": [ { "type": "platform", "fee": "4.5", "currency": "EUR", "isInclusive": true }, { "type": "network", "fee": "0.8", "currency": "EUR", "isInclusive": true }, { "type": "partner", "fee": "2.5", "currency": "EUR", "isInclusive": true } ], "partnerFee": "2.71", "partnerCurrency": "USDC", "cryptoAccountDetail": { "custodyType": "unhosted", "address": "0x535ebf73d0E20db0A72509207599C816510e11C9" } } } ``` ```json Buy · completed theme={null} { "createdAt": "2026-10-01T12:04:11Z", "eventUid": "1b0f6a9e-41c2-4f0e-9d0a-7f3a2c6e9b41", "eventType": "ramp_order.status_change", "payload": { "orderUid": "5de57551-3272-4af5-86a4-5eeae6ccc37f", "createTime": "2026-10-01T11:58:40Z", "updateTime": "2026-10-01T12:04:09Z", "externalOrderUid": "your-order-1001", "orderType": "buy", "orderStatus": "completed", "paymentStatus": "accepted", "paymentMode": "card", "srcCurrency": "EUR", "srcChain": "fiat", "srcAmount": "250", "dstCurrency": "ETH", "dstChain": "ethereum", "dstAmount": "0.0812", "dstFillAmount": "0.0809", "walletAddress": "0x535ebf73d0E20db0A72509207599C816510e11C9", "walletAddressTag": "", "transactionHash": [ "0x9c1e5b7f3a2d4e6c8b0a1f2e3d4c5b6a7980f1e2d3c4b5a69788f7e6d5c4b3a2" ], "feeDetails": [ { "type": "platform", "fee": "4.5", "currency": "EUR", "isInclusive": true }, { "type": "network", "fee": "0.8", "currency": "EUR", "isInclusive": true }, { "type": "partner", "fee": "2.5", "currency": "EUR", "isInclusive": true } ], "partnerFee": "2.71", "partnerCurrency": "USDC", "cryptoAccountDetail": { "custodyType": "unhosted", "address": "0x535ebf73d0E20db0A72509207599C816510e11C9" } } } ``` ```json Buy · payment_failed theme={null} { "createdAt": "2026-10-01T12:01:30Z", "eventUid": "e3a7d2c1-9b8f-4a6e-8d5c-2b1a0f9e8d7c", "eventType": "ramp_order.status_change", "payload": { "orderUid": "5de57551-3272-4af5-86a4-5eeae6ccc37f", "createTime": "2026-10-01T11:58:40Z", "updateTime": "2026-10-01T12:01:28Z", "externalOrderUid": "your-order-1001", "orderType": "buy", "orderStatus": "payment_failed", "paymentStatus": "rejected", "paymentMode": "card", "srcCurrency": "EUR", "srcChain": "fiat", "srcAmount": "250", "dstCurrency": "ETH", "dstChain": "ethereum", "dstAmount": "0.0812", "dstFillAmount": "0", "walletAddress": "0x535ebf73d0E20db0A72509207599C816510e11C9", "walletAddressTag": "", "feeDetails": [ { "type": "platform", "fee": "4.5", "currency": "EUR", "isInclusive": true }, { "type": "network", "fee": "0.8", "currency": "EUR", "isInclusive": true }, { "type": "partner", "fee": "2.5", "currency": "EUR", "isInclusive": true } ], "partnerFee": "2.71", "partnerCurrency": "USDC" } } ``` ```json Sell · processing theme={null} { "createdAt": "2026-10-01T14:22:05Z", "eventUid": "4f6c2a1b-8e9d-4c3b-a2f1-0e9d8c7b6a54", "eventType": "ramp_order.status_change", "payload": { "orderUid": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d", "createTime": "2026-10-01T14:05:12Z", "updateTime": "2026-10-01T14:22:03Z", "externalOrderUid": "your-order-2001", "orderType": "sell", "orderStatus": "processing", "paymentStatus": "accepted", "paymentMode": "crypto", "srcCurrency": "USDT", "srcChain": "ethereum", "srcAmount": "500", "dstCurrency": "EUR", "dstChain": "fiat", "dstAmount": "452.10", "dstFillAmount": "0", "walletAddress": "DE89370400440532013000", "walletAddressTag": "", "feeDetails": [ { "type": "platform", "fee": "5", "currency": "USDT", "isInclusive": true }, { "type": "network", "fee": "1.2", "currency": "USDT", "isInclusive": true }, { "type": "partner", "fee": "5", "currency": "USDT", "isInclusive": true } ], "partnerFee": "5", "partnerCurrency": "USDC", "bankAccountDetail": { "name": "John Doe", "iban": "DE89370400440532013000", "bic": "COBADEFFXXX", "currency": "EUR", "country": "DE" } } } ``` ```json Sell · completed theme={null} { "createdAt": "2026-10-01T14:40:51Z", "eventUid": "7d6c5b4a-3e2f-4d1c-8b0a-9f8e7d6c5b4a", "eventType": "ramp_order.status_change", "payload": { "orderUid": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d", "createTime": "2026-10-01T14:05:12Z", "updateTime": "2026-10-01T14:40:49Z", "externalOrderUid": "your-order-2001", "orderType": "sell", "orderStatus": "completed", "paymentStatus": "accepted", "paymentMode": "crypto", "srcCurrency": "USDT", "srcChain": "ethereum", "srcAmount": "500", "dstCurrency": "EUR", "dstChain": "fiat", "dstAmount": "452.10", "dstFillAmount": "452.10", "walletAddress": "DE89370400440532013000", "walletAddressTag": "", "transactionHash": [ "SEPA-REF-20261001-8F3K2" ], "feeDetails": [ { "type": "platform", "fee": "5", "currency": "USDT", "isInclusive": true }, { "type": "network", "fee": "1.2", "currency": "USDT", "isInclusive": true }, { "type": "partner", "fee": "5", "currency": "USDT", "isInclusive": true } ], "partnerFee": "5", "partnerCurrency": "USDC", "bankAccountDetail": { "name": "John Doe", "iban": "DE89370400440532013000", "bic": "COBADEFFXXX", "currency": "EUR", "country": "DE" } } } ``` ### Payload fields All amounts are decimal **strings** (for example `"0.0809"`). Parse them with a decimal type, not a float. | Field | Type | Description | | - | - | - | | `orderUid` | string | Rampnow's order id | | `externalOrderUid` | string | Your order id, as sent on Create Order | | `createTime` | string | Order creation time (ISO 8601, UTC) | | `updateTime` | string | Time of this status change (ISO 8601, UTC). Use it to order events | | `orderType` | string | `buy` or `sell` | | `orderStatus` | string | Current order status. See [Order Status](/api-reference/order-status) | | `paymentStatus` | string | Current payment status. See [Payment statuses](/api-reference/order-status#payment-statuses) | | `paymentMode` | string | Payment method used (for example `card`, `sepa`, `crypto` for a sell deposit) | | `srcCurrency` / `srcChain` | string | Source currency and chain (`fiat` for a fiat leg) | | `srcAmount` | string | Amount in the source currency | | `dstCurrency` / `dstChain` | string | Destination currency and chain (`fiat` for a fiat leg) | | `dstAmount` | string | **Quoted** destination amount | | `dstFillAmount` | string | **Actual amount delivered** (buy: crypto sent on-chain; sell: fiat paid out). `"0"` until `completed`. Reconcile on this, not `dstAmount` | | `walletAddress` | string | Destination: the crypto address (buy), or the IBAN / account number (sell) | | `walletAddressTag` | string | Memo/tag for chains that need one (XRP, XLM, TON, …). Empty otherwise | | `cryptoAccountDetail` | object | Destination crypto account (`address`, `custodyType`). Buy orders | | `bankAccountDetail` | object | Destination bank account (`name`, `iban` or `accountNumber`, `bic`, `country`, `currency`, …). Sell orders | | `transactionHash` | string\[] | Buy: on-chain transaction hash(es) of the delivery. Sell: the payout provider's reference. Left out of the payload until set | | `feeDetails` | array | Fee breakdown; each entry has `type`, `fee` (string), `currency`, `isInclusive`. See [feeDetails types](/quickstart/ramp-intent/get-quote#fee-breakdown-feedetails) | | `partnerFee` | string | Your partner fee for the order, in `partnerCurrency` | | `partnerCurrency` | string | Currency of `partnerFee`: always `USDC` | *** ## Best practices * Use an HTTPS endpoint * Verify `X-RAMPNOW-SIGN` on every request and reject failures with `401` * Reject timestamps more than 5 minutes from your clock * Log incoming webhooks (without your secret) for debugging * Return `2xx` quickly and process asynchronously * De-duplicate on `eventUid` * Apply events by `payload.updateTime` and ignore older ones * Poll [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id) as a fallback if an expected webhook doesn't arrive * Respond non-2xx only when you want Rampnow to retry * Re-send failed events from the Partner Dashboard after an outage longer than 24 hours * Contact support if webhooks stop arriving ## Testing your webhook Start your webhook server, for example on port 3000. Run your verification code against the [test vector](#test-vector) and confirm it produces the expected signature. Use a tool like [ngrok](https://ngrok.com) to get a public HTTPS URL: ```bash theme={null} ngrok http 3000 ``` Add the tunnel URL, including your webhook path, in the API Hub of your **sandbox** Partner Dashboard. Create a sandbox order and check that your endpoint receives and verifies the webhook. [Sandbox Testing](/api-reference/sandbox-testing) shows how to drive each status. ## Need help? If webhooks aren't arriving or you have questions about the payload, contact [support@rampnow.io](mailto:support@rampnow.io). # Webhooks for ramp order status changes Source: https://docs.rampnow.io/api-reference/webhooks/webhooks-for-ramp-order-status-changes /api-reference/partner_external.yaml webhook ramp_order_status_change_webhook Webhooks for ramp order status changes # Changelog Source: https://docs.rampnow.io/changelog Product updates and announcements **Ramp order responses & webhooks** * Ramp order responses (`GET /ext/ramp_order/{externalOrderUid}`) and the `ramp_order.status_change` webhook now include **`dstFillAmount`** — the actual amount delivered on-chain to the recipient. `dstAmount` remains the **quoted** amount; use `dstFillAmount` for reconciliation. * The ramp order response now also returns **`transactionHash`** (on-chain hash) and **`walletAddress`** (final recipient), and populates **`updatedAt`**. * **Webhook delivery:** an event is acknowledged on **any `2xx`** response — the body is ignored (empty/non-JSON is fine). Failed deliveries are retried up to **10 times** with exponential backoff over \~24 hours; endpoints are never auto-disabled. See [Webhooks → Delivery and retries](/api-reference/webhook#delivery-and-retries). * **Error responses:** `message` is now a stable, generic label for the error `code` and never leaks internal or upstream-provider detail. User-facing text, when applicable, is in the new **`displayError`** field. Branch on `code`. See [Error Codes](/api-reference/error-codes#error-response-body). Initial Release # Onramp & Offramp Intent API Quickstart Source: https://docs.rampnow.io/get-started/intent Create a buy or sell order from your backend and send the user to Rampnow's hosted checkout Your backend creates the order; Rampnow's hosted checkout handles verification and payment. Set `orderType` to `buy` (onramp) or `sell` (offramp). Examples run against **sandbox**. Using an AI coding agent? Connect it to these docs first: [Build with AI](/quickstart/build-with-ai). Find them in the [Partner Dashboard](https://partner.rampnow.io) under **API Hub**. Keep the secret on your server. No account yet? Email [support@rampnow.io](mailto:support@rampnow.io). Every call carries `X-RAMPNOW-API-KEY`, `X-RAMPNOW-TIMESTAMP` and `X-RAMPNOW-SIGN` (hex HMAC-SHA256 of `timestamp + METHOD + path + body`). ```javascript Node.js theme={null} const crypto = require("crypto"); const BASE_URL = "https://api.sandbox.rampnow.io"; async function rampnow(method, path, payload) { const body = payload ? JSON.stringify(payload) : ""; const timestamp = String(Math.floor(Date.now() / 1000)); const signature = crypto .createHmac("sha256", process.env.RAMPNOW_API_SECRET) .update(timestamp + method + path + body) .digest("hex"); const res = await fetch(BASE_URL + path, { method, headers: { "Content-Type": "application/json", "X-RAMPNOW-API-KEY": process.env.RAMPNOW_API_KEY, "X-RAMPNOW-TIMESTAMP": timestamp, "X-RAMPNOW-SIGN": signature, }, body: body || undefined, }); return res.json(); } ``` Other languages and a test vector: [Authentication](/api-reference/authentication). ```javascript theme={null} const quote = await rampnow("POST", "/api/partner/v1/ext/ramp_order/quote", { orderType: "buy", srcCurrency: "EUR", srcChain: "fiat", srcAmount: "250", dstCurrency: "ETH", dstChain: "ethereum", }); ``` ```javascript theme={null} const quote = await rampnow("POST", "/api/partner/v1/ext/ramp_order/quote", { orderType: "sell", srcCurrency: "USDT", srcChain: "ethereum", srcAmount: "500", dstCurrency: "EUR", dstChain: "fiat", }); ``` ```javascript theme={null} const { data } = await rampnow("POST", "/api/partner/v1/ext/ramp_order/checkout", { orderType: "buy", externalOrderUid: "order-1001", srcCurrency: "EUR", srcChain: "fiat", srcAmount: "250", dstCurrency: "ETH", dstChain: "ethereum", returnUrl: "https://your-app.com/orders/order-1001", user: { firstName: "John", lastName: "Doe", email: "john@example.com", phone: "+491234567890", dateOfBirth: "1990-01-15", gender: "male", country: "DE", }, wallet: { crytpoAccountDetail: { address: "0xUserWalletAddress", custodyType: "unhosted" }, }, }); ``` ```javascript theme={null} const { data } = await rampnow("POST", "/api/partner/v1/ext/ramp_order/checkout", { orderType: "sell", externalOrderUid: "order-1001", srcCurrency: "USDT", srcChain: "ethereum", srcAmount: "500", dstCurrency: "EUR", dstChain: "fiat", returnUrl: "https://your-app.com/orders/order-1001", user: { firstName: "John", lastName: "Doe", email: "john@example.com", phone: "+491234567890", dateOfBirth: "1990-01-15", gender: "male", country: "DE", }, wallet: { bankAccountDetail: { name: "John Doe", iban: "DE89370400440532013000", bic: "COBADEFFXXX", country: "DE", currency: "EUR", }, }, }); ``` The response holds `orderUid`, `userUid` and `redirectUrl`. ```javascript theme={null} window.location.href = data.redirectUrl; ``` The user verifies and pays (buy) or sends crypto (sell), then lands on your `returnUrl`. Add your webhook URL in the Partner Dashboard under **API Hub**. Rampnow posts on every status change; `completed` means done. Or poll: ```javascript theme={null} const order = await rampnow("GET", "/api/partner/v1/ext/ramp_order/order-1001"); ``` See [Webhook](/api-reference/webhook) and [Order Status](/api-reference/order-status). Field reference, fees, retries and the hosted checkout in detail # Onramp & Offramp SDK Quickstart Source: https://docs.rampnow.io/get-started/sdk Open Rampnow's buy and sell flow from JavaScript with @rampnow/sdk The SDK opens the widget as an overlay, with typed config and browser events. No backend or signing needed. Set `orderType` to `buy` (onramp) or `sell` (offramp). Examples run against **sandbox**. Using an AI coding agent? Connect it to these docs first: [Build with AI](/quickstart/build-with-ai). ```bash npm theme={null} npm install @rampnow/sdk ``` ```bash pnpm theme={null} pnpm add @rampnow/sdk ``` Use your public key (`pk_...`) from the [Partner Dashboard](https://partner.rampnow.io) under **API Hub**. ```javascript theme={null} import { RampnowSdk, RampnowEventType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', widgetUrl: 'https://app.sandbox.rampnow.io', orderType: 'buy', srcCurrency: 'EUR', srcChain: 'fiat', srcAmount: '250', dstCurrency: 'ETH', dstChain: 'ethereum', walletAddress: '0xUserWalletAddress', }) ``` ```javascript theme={null} import { RampnowSdk, RampnowEventType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', widgetUrl: 'https://app.sandbox.rampnow.io', orderType: 'sell', srcCurrency: 'USDT', srcChain: 'ethereum', srcAmount: '500', dstCurrency: 'EUR', dstChain: 'fiat', }) ``` Drop `widgetUrl` in production; it defaults to `https://app.rampnow.io`. ```javascript theme={null} sdk.on(RampnowEventType.ORDER_COMPLETED, (e) => { console.log('Order completed:', e.payload.orderUid) }) sdk.init() ``` Add your webhook URL in the Partner Dashboard under **API Hub**. Rampnow posts on every order status change. See [Webhook](/api-reference/webhook). Embedded and redirect modes, every event, commands and the React hook # Onramp & Offramp Widget Quickstart Source: https://docs.rampnow.io/get-started/widget Embed Rampnow's buy and sell flow with a URL or an iframe No backend and no signing: one URL with your public key. Set `orderType` to `buy` (onramp) or `sell` (offramp). Examples run against **sandbox**. Using an AI coding agent? Connect it to these docs first: [Build with AI](/quickstart/build-with-ai). Copy your public key (`pk_...`) from the [Partner Dashboard](https://partner.rampnow.io) under **API Hub**. ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=ETH&dstChain=ethereum&walletAddress=0xUserWalletAddress ``` ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=sell&srcCurrency=USDT&srcChain=ethereum&srcAmount=500&dstCurrency=EUR&dstChain=fiat ``` Add `externalOrderId` to tie the order to your records. All parameters: [Parameters](/quickstart/ramp-widget/parameters). ```html iFrame theme={null} ``` ```javascript Redirect theme={null} window.location.href = "WIDGET_URL"; ``` Add your webhook URL in the Partner Dashboard under **API Hub**. Rampnow posts on every order status change. See [Webhook](/api-reference/webhook). Every URL parameter, iframe and redirect modes # Build with Rampnow. Source: https://docs.rampnow.io/index Add onramp and offramp to your product: let users buy crypto with fiat and sell crypto for fiat, with one integration
bitcoin bnb

Build with Rampnow.

Let your users buy crypto with fiat and sell crypto for fiat without leaving your product. One integration covers both directions. Rampnow runs payments, KYC, liquidity, on-chain delivery and bank payouts.

blockchains tokens Cards, bank transfers and local payment methods
Choose an integration → API reference
USD Coin ethereum SOL Tether

Pick how you integrate

All three support buy and sell, and share the same order lifecycle, webhooks and sandbox. They differ in how much of the experience you build. Compare them side by side.

**Least effort.** Open Rampnow's buy and sell flow with a URL or an iframe. Public key only, no backend. **For web apps.** Open the same flow from JavaScript with `@rampnow/sdk`. Adds browser events, open/close control and a React hook. **Most control.** Build your own quote and checkout screens. Your backend creates signed orders and redirects users to Rampnow's hosted checkout.

From sandbox to live

Your public key, API key and secret are in the Partner Dashboard under **API Hub**. No account yet? Contact support below. Follow your integration's quickstart, then drive orders to completed, failed or expired with the payment simulator. Work through the go-live checklist, then ask support for production access.

Plan your launch

For product managers: where you can launch, what users see and what they can pay with.

Where your users can be based, and which countries are restricted. Tokens users can buy and sell, and the chains they run on. How users pay on buy orders, how they get paid on sell orders, and the limits. Currencies users can pay with on buy orders and receive on sell orders. Every screen your users go through, from quote to delivery. Coverage, liquidity routing and what Rampnow handles for you.

Build and operate

For developers: the references you will use most once the integration is running.

Payload, signature verification and retries for every order status change. Every status an order moves through, and which ones are final. Sign Intent API requests with HMAC-SHA256. Examples in several languages. What each error means and which ones are safe to retry. Call the Intent API from Postman, with signing set up for you. Connect these docs to Claude, Cursor or ChatGPT.

Need help?

Short answers to common integration and product questions. Integration questions, sandbox access and production keys. Pricing, coverage and partnership.

Also from Rampnow: crypto-to-crypto swaps and cross-chain transfers are available through Hypermid. Rampnow Wallet, custodial wallets for your users, is coming soon.

# Build with AI Source: https://docs.rampnow.io/quickstart/build-with-ai Connect Rampnow's docs to Claude, Cursor, ChatGPT and other AI tools so your agent builds against current docs Rampnow's docs are published in formats AI tools can read directly. Connect your coding agent to them, and it works from the current API instead of guessing from its training data or from pages you paste in. Your agent searches and reads the docs while it works An index of every page, or the whole site in one file Any page as clean Markdown A short brief that tells agents how to integrate Rampnow ## MCP server The docs run a hosted [Model Context Protocol](https://modelcontextprotocol.io) server at: ```text theme={null} https://docs.rampnow.io/mcp ``` It gives your agent two read-only tools: one searches the docs, the other reads full pages and the OpenAPI spec. It needs no authentication. ```bash theme={null} claude mcp add --transport http rampnow-docs https://docs.rampnow.io/mcp ``` In Claude, open **Settings → Connectors**, choose **Add custom connector**, name it `Rampnow Docs` and enter `https://docs.rampnow.io/mcp` as the URL. Add the server to `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project): ```json theme={null} { "mcpServers": { "rampnow-docs": { "url": "https://docs.rampnow.io/mcp" } } } ``` Add the server to `.vscode/mcp.json`: ```json theme={null} { "servers": { "rampnow-docs": { "type": "http", "url": "https://docs.rampnow.io/mcp" } } } ``` In ChatGPT, open **Settings → Apps & Connectors**, create a connector and enter `https://docs.rampnow.io/mcp` as the server URL. Custom connectors need developer mode turned on. Clients that support discovery can find the server at `https://docs.rampnow.io/.well-known/mcp`. You can also add the server from any docs page: open the page menu at the top of the page and choose **Connect to Cursor**, **Connect to VS Code** or **Copy MCP server URL**. The MCP server searches the documentation only. It doesn't call the Rampnow API, see your orders or use your API keys. ## llms.txt Two files follow the [llms.txt](https://llmstxt.org) convention: | File | Contents | Use it when | | - | - | - | | [`/llms.txt`](https://docs.rampnow.io/llms.txt) | Every page, with its title, description and Markdown link | Your agent fetches the pages it needs | | [`/llms-full.txt`](https://docs.rampnow.io/llms-full.txt) | The full text of every page in one file | You give a model the whole site in one go | To ask questions about the full docs from your own code, send `llms-full.txt` as a cached system prompt. Follow-up questions then reuse the cache, which costs less and responds faster: ```python Python theme={null} import urllib.request import anthropic with urllib.request.urlopen("https://docs.rampnow.io/llms-full.txt") as res: docs = res.read().decode("utf-8") client = anthropic.Anthropic() response = client.beta.messages.create( model="claude-opus-5-5", max_tokens=16000, betas=["server-side-fallback-2026-07-01"], fallbacks="default", system=[ {"type": "text", "text": "Answer using the Rampnow documentation below."}, {"type": "text", "text": docs, "cache_control": {"type": "ephemeral"}}, ], messages=[ {"role": "user", "content": "How do I sign a request to the quote endpoint?"} ], ) if response.stop_reason == "refusal": print("The request was declined.") else: print("".join(block.text for block in response.content if block.type == "text")) ``` ## Markdown export Every page is also available as Markdown. Add `.md` to its URL: * [`/get-started/intent.md`](https://docs.rampnow.io/get-started/intent.md) * [`/api-reference/authentication.md`](https://docs.rampnow.io/api-reference/authentication.md) * [`/api-reference/webhook.md`](https://docs.rampnow.io/api-reference/webhook.md) A request for the normal page URL with an `Accept: text/markdown` header returns the same Markdown: ```bash theme={null} curl -H "Accept: text/markdown" https://docs.rampnow.io/api-reference/authentication ``` To get a page without leaving the site, open the page menu at the top of any page. From there you can copy the page as Markdown or open it in ChatGPT, Claude or Perplexity. ## Agent skill [`/skill.md`](https://docs.rampnow.io/skill.md) is a short brief for coding agents, in the [Agent Skills](https://agentskills.io) format. It covers which product to use, authentication, the order flow, statuses and common mistakes. It links to the full pages for everything else. Install it into your agent with: ```bash theme={null} npx skills add https://docs.rampnow.io ``` ## Recommended setup Add the [MCP server](#mcp-server) to your editor or agent so it can look things up as it works. Rampnow has two integrations that don't mix. Start your agent on the right quickstart: * **Widget or SDK** (no backend, public key only): [Widget](/get-started/widget) or [SDK](/get-started/sdk) * **Intent API** (your backend creates signed orders): [Intent API](/get-started/intent) For a single large task, such as writing a whole integration, put [`llms-full.txt`](#llms-txt) in context with prompt caching. Check generated requests against the [API reference](/api-reference/overview). Test the signing code against the [test vector](/api-reference/authentication#test-vector). Run the [sandbox test plan](/api-reference/sandbox-testing#suggested-test-run) before going live. Never paste your API secret into a prompt or an agent's context. Keep it in your server's environment, and have your agent write code that reads it from there. # Frequently Asked Questions Source: https://docs.rampnow.io/quickstart/faq Short answers to common integration questions, with links to the full details ## Choosing an integration Use the **Widget** for the fastest launch with a link or an iframe. Use the **SDK** when you also want browser events, open/close control or a React hook. Use **Intent** when you want to build the pre-checkout screens yourself and create orders from your backend. See [Integrations Overview](/quickstart/integrations/overview). The SDK is a JavaScript wrapper around the widget. Both run the same flow and take the same [parameters](/quickstart/ramp-widget/parameters). The SDK adds [events and commands](/quickstart/ramp-sdk/events), overlay and embedded [display modes](/quickstart/ramp-sdk/display-modes), and TypeScript types. No. Every integration covers both. Set `orderType` to `buy` (onramp) or `sell` (offramp) per order. ## Keys and environments The widget and SDK only need your **public partner key**, passed as `apiKey`. It is safe in client-side code. The Intent API uses an **API key and secret** to sign server-to-server requests. Never expose the secret. See [Authentication](/api-reference/authentication). No. Keys are environment-scoped. Use sandbox keys with `*.sandbox.rampnow.io` hosts and production keys with production hosts. See [Authentication](/api-reference/authentication#environments). ## Testing Use the sandbox. It runs the same flow with simulated payments and testnet tokens. See [Sandbox Testing](/api-reference/sandbox-testing) and the [Payment Simulator](/api-reference/payment-simulator). Sandbox has no self-serve way to force a KYC outcome yet. Email [support@rampnow.io](mailto:support@rampnow.io). See [Sandbox Testing](/api-reference/sandbox-testing#kyc). ## Orders and webhooks Use [webhooks](/api-reference/webhook): Rampnow sends one on every status change. Intent integrations can also [poll the order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id). SDK events are useful for your UI, but confirm on your backend. See [Order Status](/api-reference/order-status) for which statuses are final. Yes. A failed delivery is retried up to 10 times over roughly 24 hours. De-duplicate on `eventUid`, which stays the same across retries. See [Delivery and retries](/api-reference/webhook#delivery-and-retries). No. Rampnow operations either retries it or blocks and refunds it. Wait for the next webhook. See [Order Status](/api-reference/order-status). No. Confirm the status from a webhook or by polling the order. See [Handling rules](/api-reference/order-status#handling-rules). Pass your own order ID: `externalOrderId` on the widget or SDK, `externalOrderUid` on the Intent API. Either way it comes back as `externalOrderUid` on webhooks. No. Sending the same Create Order request twice creates two orders. On a timeout or `5xx`, look the order up by `externalOrderUid` first and retry only if none exists. See [Create the Order](/quickstart/ramp-intent/create-order). ## Customising the order Yes. Pass them as widget parameters or SDK config, for example `dstCurrency`, `dstChain`, `srcAmount` and `walletAddress`. Make them read-only with `lockFields`. See [URL parameters](/quickstart/ramp-widget/parameters). Your account has a partner fee agreed with Rampnow, applied to every order. With Intent you can also override it per request. See [Partner fee](/quickstart/ramp-intent/get-quote#partner-fee-markup). ## Coverage and compliance See [Supported Crypto Currencies](/quickstart/resources/supported-assets), [Supported Fiat Currencies](/quickstart/resources/supported-fiat), [Blockchain List](/quickstart/resources/blockchain-list), [Payment Methods & Limits](/quickstart/resources/payment-methods) and [Supported Countries](/quickstart/resources/supported-countries). Live data on those pages comes from the Rampnow config API. Offramp payouts are currently live for EUR via SEPA. See [Payment Methods & Limits](/quickstart/resources/payment-methods#offramp-payout-timing). Rampnow does, inside the widget or hosted checkout. Users who are already verified skip it. See [User Flow](/quickstart/ramp/flow). ## Still stuck? Email [support@rampnow.io](mailto:support@rampnow.io) with your partner account and, for order issues, the order ID. # Integrations Overview Source: https://docs.rampnow.io/quickstart/integrations/overview Three ways to add Rampnow onramp and offramp to your product, and how to choose between them Rampnow lets your users buy and sell crypto without leaving your product. Every integration covers both directions: * **Onramp (buy):** the user pays with fiat and receives crypto in their wallet. * **Offramp (sell):** the user sends crypto and receives fiat in their bank account. You pick the direction per order with `orderType` (`buy` or `sell`). Rampnow runs payment, KYC, liquidity, on-chain delivery and bank payouts behind it. ## Choose an integration There are three ways to integrate. They differ in how much of the experience you build yourself.
Embed the Rampnow widget with a URL or an iframe. No code beyond a link Open the Rampnow widget from JavaScript, with typed config, browser events and a React hook Create orders from your backend and send the user to Rampnow's hosted checkout
| | Widget | SDK | Intent API | | - | - | - | - | | **You build** | A URL or an iframe with query parameters | A few lines of JavaScript with `@rampnow/sdk` | Your own screens, plus server-to-server quote and order calls | | **Rampnow handles** | The whole buy and sell UI, payment, KYC | The whole buy and sell UI, payment, KYC | Hosted checkout: payment, KYC, settlement | | **Credentials** | Public partner key (`apiKey`) | Public partner key (`apiKey`) | API key and secret, signed requests | | **Backend needed** | No (webhooks recommended) | No (webhooks recommended) | Yes | | **Order updates** | Webhooks | Browser events and webhooks | Webhooks and polling | | **Best for** | The fastest launch with the least code | Web apps that want events, programmatic control and React | Platforms that own the pre-checkout journey and hold user data | The SDK is a wrapper around the widget. Both open the same Rampnow flow and accept the same [parameters](/quickstart/ramp-widget/parameters). Start with the widget if you only need a link or an iframe; move to the SDK when you need events or open/close control. All three share the same order lifecycle, [webhooks](/api-reference/webhook), [order statuses](/api-reference/order-status), [sandbox](/api-reference/sandbox-testing) and [supported assets, payment methods and countries](/quickstart/resources/supported-assets). ## What you handle and what Rampnow handles
* Your partner API key on every widget session * The entry point in your app, plus any asset, amount or wallet address you pre-fill * Your own order ID (`externalOrderId`), to reconcile orders with your records * A webhook endpoint that receives order status changes * The whole user interface inside the widget * User verification: email/OTP and KYC, skipped for users who are already verified * Payment capture on buy orders, crypto collection on sell orders, FX and liquidity routing * Delivery of crypto to the user's wallet, or fiat to the user's bank account * AML and on-chain risk screening on every order
* Request signing with your API key and secret * The quote, asset picker and user details screens * Creating the order server-to-server and redirecting the user * A webhook endpoint, or polling, to track the order * The hosted checkout page: payment and KYC * Payment capture, crypto collection, FX and liquidity routing * Delivery of crypto or fiat, and the return to your `returnUrl` * AML and on-chain risk screening on every order
## Why choose Rampnow?
** blockchains** and ** assets** with EVM and non-EVM support Cards, bank transfers, Apple Pay, Google Pay and **local APMs** for buying. SEPA bank payouts for selling Same order lifecycle and webhooks for buy and sell orders High-availability routing with **predictable fees** and real-time status updates
## Core capabilities ### Instant quotes * Real-time FX rates * Network and payment method fees * Slippage controls * Guaranteed execution rates ### Global payment acceptance * **Cards**: Visa, Mastercard * **Digital wallets**: Apple Pay, Google Pay * **Bank transfers**: SEPA, ACH, PIX, UPI * **Local APMs**: Auto-adjusted by region and currency ### Blockchain delivery * Liquidity provider routing * On-chain execution preparation * Direct wallet delivery * Full transaction metadata confirmation ### Security * Device fingerprinting * Behavioral risk scoring * Payment fraud checks * Beneficiary wallet screening ### Quote & eligibility checks Before a sell order is created, Rampnow validates the asset and chain, checks corridor availability and returns the fiat amount, fees and FX, estimated delivery time and applicable limits. You can show users **exactly what they will receive** before they send anything. ### Asset collection * **User-initiated transfer**: the user sends assets from their wallet to a Rampnow-provided address. * **Platform-initiated transfer**: you send assets from a custody or omnibus wallet after the user confirms. Each order is tied to a unique reference for deterministic matching and reconciliation. ### FX & treasury routing Once assets are confirmed on-chain, Rampnow converts them into EUR using connected liquidity, routes funds via SEPA and applies risk-based checks and velocity limits. ### Fiat payout Users receive fiat by **bank transfer** (SEPA). Each order emits a webhook on every status change. See [Order Status](/api-reference/order-status). ### Reporting & reconciliation * Order-level records (asset, chain, user, fiat, fees) * Payout references and bank trace IDs * Exportable data for accounting and tax reporting ## Use cases
Let users buy assets inside the wallet, and cash out to their bank when they need fiat. Support instant fiat deposits into digital asset accounts, and withdrawals back to the bank. Provide smooth purchase flows for first-time Web3 users without crypto friction. Allow players to acquire in-game assets or tokens with fiat for better onboarding. Add digital asset investment and cash-out features to banking and neobank applications. Use the onramp and offramp as entry and exit points for cross-border transfers and settlement.
## From sandbox to live Ask [support@rampnow.io](mailto:support@rampnow.io) for a partner account. Your keys are in the [Partner Dashboard](https://partner.rampnow.io) under **API Hub**. Follow the [Widget](/quickstart/ramp-widget/overview), [SDK](/quickstart/ramp-sdk/overview) or [Intent](/quickstart/ramp-intent/overview) guide. Set `orderType` to `buy` or `sell`. Add your endpoint in the Partner Dashboard and verify signatures. See the [Webhook guide](/api-reference/webhook). Cover completed, failed and expired orders in both directions. See [Sandbox Testing](/api-reference/sandbox-testing). Work through the [Go-Live Checklist](/api-reference/go-live-checklist), then ask support for production access. # Platform overview Source: https://docs.rampnow.io/quickstart/learn/overview Modular building blocks for global fiat ↔ blockchain money movement With a single integration, accept fiat payments, convert to digital assets, and access supported blockchains and pay users out in local currencies. ## Core Products
Embed the Rampnow widget or SDK so your users buy crypto with fiat and sell crypto to their bank account Create buy and sell orders from your backend and send the user to Rampnow's hosted checkout Route swaps and cross-chain transfers across supported networks with optimized pricing A non-custodial smart wallet for retail users with built-in AA, multi-chain support, secure signing and direct onramp integration
## Integration models Rampnow offers three ways to integrate: the **SDK**, the **Widget** and the **Intent** API. Pick based on how much of the UI you want to own. See [Integrations Overview](/quickstart/integrations/overview) for a side-by-side comparison. ## Onramp: Accept fiat and deliver digital assets See the [Payment Methods & Limits](/quickstart/resources/payment-methods) page for the current list of cards, bank transfers and local payment methods by region. * ** blockchains** supported * ** tokens** available * **Multi-chain coverage** across all major networks View complete Onramp documentation → ## Offramp: Cash out to bank accounts * EUR (expanding continuously) * SEPA transfers * Card payouts (Coming Soon) * Wallet withdrawals * Exchange cash-outs * Earnings payouts * Asset exits
Offramp flow Supported fiat Settlement options
*** ## Swap & Bridge For cross-chain swaps and bridging, explore [Hypermid](https://docs.hypermid.io), our dedicated cross-chain infrastructure product. Hypermid provides infrastructure for swapping assets and bridging between supported blockchain networks through a unified API. [Learn more about Hypermid →](https://docs.hypermid.io) *** ## Customers Customer objects for end-users and businesses with full lifecycle management *** ## Next Steps
New to Rampnow? Start here View supported payment rails See all supported chains Need support?
Rampnow is designed to be the **invisible infrastructure** behind your product—so you can focus on UX, while we handle payments and routing. # Why Rampnow Source: https://docs.rampnow.io/quickstart/learn/why-rampnow Unified infrastructure for global fiat ↔ blockchain money movement ## The onboarding challenge Today's fiat-to-blockchain landscape is fragmented, slow, and heavily regional. Developers often struggle to integrate multiple payment providers, each burdened by different local banking rules, limited network support, and restrictive regional coverage. For platforms requiring global scalability, these fragmented rails result in high payment failure rates and friction-filled user onboarding. **The result:** Businesses struggle to offer a smooth, global money movement experience, losing users at the checkout and facing massive integration overhead. ## Rampnow's solution Rampnow eliminates this complexity by providing complete onramp and offramp infrastructure with advanced liquidity routing through a **single, globally accessible API**. We handle payments and settlements end-to-end. Our unified infrastructure supports ** blockchains**, ** tokens** and multi-currency fiat settlements. This drastically reduces vendor fragmentation, increases conversion, and simplifies global deployment. With Rampnow, businesses can launch modern money-movement products faster, with significantly lower risk and minimal operational overhead. ## What you can build ### Onramp Enable your users to purchase digital assets instantly using: * **Cards:** Visa, Mastercard, Apple Pay, Google Pay * **Bank transfers:** SEPA, ACH, PIX, UPI * **Local payment methods:** Country-specific options across EU, APAC, LATAM, Africa * **Multi-chain support:** blockchains and tokens ### Offramp Let users convert digital assets back to fiat and receive funds directly: * **Supported currencies:** EUR (expanding continuously) * **Settlement methods:** SEPA (expanding continuously) * **Automated liquidity routing:** Best rate execution per asset and chain Useful for wallets, dApps, exchanges and marketplaces where users need to withdraw funds securely. ### Choose how to integrate onramp and offramp Both directions are available two ways. Pick based on how much of the UI you want to own:
Embed the Rampnow widget or SDK so your users buy crypto with fiat and sell crypto to their bank account Create buy and sell orders from your backend and send the user to Rampnow's hosted checkout
### Swap & Bridge For cross-chain swaps and bridging, explore [Hypermid](https://docs.hypermid.io), our dedicated cross-chain infrastructure product. Hypermid enables seamless asset movement across tokens and blockchains through a unified API, with multi-source liquidity aggregation, route selection, cross-chain bridging and real-time execution visibility. ## Get started [Make your first API request in minutes](/get-started/intent) Embed the [widget](/quickstart/ramp-widget/overview) or [SDK](/quickstart/ramp-sdk/overview) in your app, or [create orders from your backend with the Intent API](/quickstart/ramp-intent/overview) [Track order status in real time with webhooks](/api-reference/webhook) [Visit our API reference.](/api-reference/overview) ## Why choose Rampnow? One API for onramp and offramp, with Hypermid for cross-chain swaps and bridging Large network coverage: blockchains, tokens Fast fiat settlement across supported currencies Supports cards, SEPA, UPI and local rails Optimized payment routing Reliable uptime and monitoring Simple integration with clear documentation ## Getting help Questions? We're here to help you go live quickly. **Technical Support:** [support@rampnow.io](mailto:support@rampnow.io) **Sales:** [partnership@rampnow.io](mailto:partnership@rampnow.io) # Step 3: Create the Order Source: https://docs.rampnow.io/quickstart/ramp-intent/create-order Create a buy or sell checkout order with the user's details in one call This is the core call of the Intent flow. It creates the order **and** the customer, and returns the hosted checkout link. Create Order is **not idempotent**. Rampnow doesn't de-duplicate on `externalOrderUid`, so sending the same request twice creates two orders. If the call times out or returns a `5xx`, call [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id) with your `externalOrderUid` first, and retry only if `orderList` is empty. ## Request Call [Create Checkout Order](/api-reference/onramp-&-offramp-intent/create-a-checkout-order). Buy and sell orders use the same endpoint; `orderType`, the currency pair and the `wallet` object differ. ```bash cURL theme={null} curl --request POST \ --url https://api.rampnow.io/api/partner/v1/ext/ramp_order/checkout \ --header 'Content-Type: application/json' \ --header 'X-RAMPNOW-API-KEY: YOUR_API_KEY' \ --header 'X-RAMPNOW-TIMESTAMP: UNIX_SECONDS' \ --header 'X-RAMPNOW-SIGN: HMAC_SHA256_SIGNATURE' \ --data '{ "orderType": "buy", "externalOrderUid": "your-unique-order-id", "srcCurrency": "EUR", "srcChain": "fiat", "srcAmount": "250", "dstCurrency": "ETH", "dstChain": "ethereum", "returnUrl": "https://your-app.com/orders/your-unique-order-id", "user": { "firstName": "John", "lastName": "Doe", "email": "john@example.com", "phone": "+491234567890", "dateOfBirth": "1990-01-15", "gender": "male", "country": "DE" }, "wallet": { "crytpoAccountDetail": { "address": "0xUserDestinationWalletAddress", "custodyType": "unhosted" } } }' ``` #### Order fields | Field | Required | Description | | - | - | - | | `orderType` | Required | `buy` for onramp | | `externalOrderUid` | Required | Your unique order id, used to look the order up later. **Not de-duplicated**: a repeated call creates another order | | `srcCurrency` / `srcChain` | Required | Fiat currency the user pays with (e.g. `EUR`) on chain `fiat` | | `srcAmount` / `dstAmount` | One of | Set either the source or destination amount, not both | | `dstCurrency` / `dstChain` | Optional | Crypto asset + network to deliver (e.g. `ETH` on `ethereum`). Send both or neither; **required** when you send `wallet` | | `user` | Required | Customer details (see below) | | `wallet.crytpoAccountDetail` | Optional | The user's destination crypto wallet `{ address, custodyType }` (`custodyType`: `unhosted` or `hosted`). Note the field name keeps the `crytpo` spelling. If you omit `wallet`, the crypto goes to **your partner wallet**, and the user can't enter one at checkout. | | `addressTag` | Optional | Memo/tag for chains that need one (XRP, XLM, TON, …) | | `paymentMode` | Optional | Preferred payment method. If omitted, the user picks one during checkout | | `returnUrl` | Optional | Where Rampnow sends the user after checkout. Rampnow appends `?status=`; include your own order id in the URL. | | `webhookUrl` | Optional | Not currently used for delivery: webhooks always go to the account-level URL set in API Hub. Leave it unset | | `partnerPctFee` / `partnerFixedFee` | Optional | Per-order partner fee override. See [Partner fee](/quickstart/ramp-intent/get-quote#partner-fee-markup) | ```bash cURL theme={null} curl --request POST \ --url https://api.rampnow.io/api/partner/v1/ext/ramp_order/checkout \ --header 'Content-Type: application/json' \ --header 'X-RAMPNOW-API-KEY: YOUR_API_KEY' \ --header 'X-RAMPNOW-TIMESTAMP: UNIX_SECONDS' \ --header 'X-RAMPNOW-SIGN: HMAC_SHA256_SIGNATURE' \ --data '{ "orderType": "sell", "externalOrderUid": "your-unique-order-id", "srcCurrency": "USDT", "srcChain": "ethereum", "srcAmount": "500", "dstCurrency": "EUR", "dstChain": "fiat", "returnUrl": "https://your-app.com/orders/your-unique-order-id", "user": { "firstName": "John", "lastName": "Doe", "email": "john@example.com", "phone": "+491234567890", "dateOfBirth": "1990-01-15", "gender": "male", "country": "DE" }, "wallet": { "bankAccountDetail": { "name": "John Doe", "iban": "DE89370400440532013000", "bic": "COBADEFFXXX", "country": "DE", "currency": "EUR" } } }' ``` #### Order fields | Field | Required | Description | | - | - | - | | `orderType` | Required | `sell` for offramp | | `externalOrderUid` | Required | Your unique order id, used to look the order up later. **Not de-duplicated**: a repeated call creates another order | | `srcCurrency` / `srcChain` | Required | Crypto asset + network the user sells (e.g. `USDT` on `ethereum`) | | `srcAmount` / `dstAmount` | One of | Set either the source or destination amount, not both | | `dstCurrency` / `dstChain` | Optional | Fiat payout currency (e.g. `EUR`) on chain `fiat`. Send both or neither; **required** when you send `wallet` | | `user` | Required | Customer details (see below) | | `wallet.bankAccountDetail` | Optional | Payout bank account: `name`, `iban` or `accountNumber`, `bic`, `bankName`, `country`, `currency`, … Required fields vary by payout currency (e.g. `taxId` + IFSC in `bic` for INR). If you omit `wallet`, the payout goes to **your partner account**. | | `paymentMode` | Optional | Preferred payment method. If omitted, the user picks one during checkout | | `returnUrl` | Optional | Where Rampnow sends the user after checkout. Rampnow appends `?status=`; include your own order id in the URL. | | `webhookUrl` | Optional | Not currently used for delivery: webhooks always go to the account-level URL set in API Hub. Leave it unset | | `partnerPctFee` / `partnerFixedFee` | Optional | Per-order partner fee override. See [Partner fee](/quickstart/ramp-intent/get-quote#partner-fee-markup) | ### User details There is **no separate user-creation call**. Rampnow creates the customer from the `user` object on the order, or matches an existing customer by `email`, and returns their `userUid` in the response. | `user` field | Required | Description | | - | - | - | | `firstName` / `lastName` | Required | Customer's legal name | | `email` | Required | Customer's email. Used to match returning customers | | `phone` | Required | Phone number with country code (e.g. `+491234567890`) | | `dateOfBirth` | Required | Date of birth in `YYYY-MM-DD` format | | `gender` | Required | `male`, `female` or `unknown` | | `country` | Required | ISO 3166-1 alpha-2 country code (e.g. `DE`) | | `address` | Optional | Residential address (`line1`, `city`, `postalCode`, `country`, …) | ## Response The response is the same for buy and sell orders: ```json theme={null} { "code": 0, "message": "success", "traceId": "trace-id", "data": { "orderUid": "rampnow-order-uid", "userUid": "rampnow-user-uid", "redirectUrl": "https://app.rampnow.io/..." } } ``` | Field | Description | | - | - | | `orderUid` | Rampnow's order id. Also included in webhooks for this order | | `userUid` | Rampnow's customer id: existing, or newly created from `user` | | `redirectUrl` | Hosted checkout URL. Send the user here next | Send the user to the hosted checkout # Step 2: Get a Quote Source: https://docs.rampnow.io/quickstart/ramp-intent/get-quote Price a buy or sell order before creating it Quotes are **non-binding**. They show the user what they will get. The final price is set when the user confirms on the hosted page. ## Request Call [Get Quote](/api-reference/onramp-&-offramp-intent/get-a-quote). The request is the same for both directions; `orderType` and the source/destination pair change. ```bash cURL theme={null} curl --request POST \ --url https://api.rampnow.io/api/partner/v1/ext/ramp_order/quote \ --header 'Content-Type: application/json' \ --header 'X-RAMPNOW-API-KEY: YOUR_API_KEY' \ --header 'X-RAMPNOW-TIMESTAMP: UNIX_SECONDS' \ --header 'X-RAMPNOW-SIGN: HMAC_SHA256_SIGNATURE' \ --data '{ "orderType": "buy", "srcCurrency": "EUR", "srcChain": "fiat", "srcAmount": "250", "dstCurrency": "ETH", "dstChain": "ethereum" }' ``` | Field | Required | Description | | - | - | - | | `orderType` | Required | `buy` for onramp | | `srcCurrency` / `srcChain` | Required | Fiat currency the user pays with (e.g. `EUR`) on chain `fiat` | | `dstCurrency` / `dstChain` | Required | Crypto asset + network to deliver (e.g. `ETH` on `ethereum`) | | `srcAmount` / `dstAmount` | One of | Set **either** the amount the user pays (`srcAmount`) **or** the amount they receive (`dstAmount`). The other side is computed | | `paymentMode` | Optional | Price against a specific payment method. Quotes can differ by method | | `partnerPctFee` / `partnerFixedFee` | Optional | Per-request partner fee override (see below) | ```bash cURL theme={null} curl --request POST \ --url https://api.rampnow.io/api/partner/v1/ext/ramp_order/quote \ --header 'Content-Type: application/json' \ --header 'X-RAMPNOW-API-KEY: YOUR_API_KEY' \ --header 'X-RAMPNOW-TIMESTAMP: UNIX_SECONDS' \ --header 'X-RAMPNOW-SIGN: HMAC_SHA256_SIGNATURE' \ --data '{ "orderType": "sell", "srcCurrency": "USDT", "srcChain": "ethereum", "srcAmount": "500", "dstCurrency": "EUR", "dstChain": "fiat" }' ``` | Field | Required | Description | | - | - | - | | `orderType` | Required | `sell` for offramp | | `srcCurrency` / `srcChain` | Required | Crypto asset + network the user sells (e.g. `USDT` on `ethereum`) | | `dstCurrency` / `dstChain` | Required | Fiat payout currency (e.g. `EUR`) on chain `fiat` | | `srcAmount` / `dstAmount` | One of | Set **either** the amount the user sells (`srcAmount`) **or** the amount they receive (`dstAmount`). The other side is computed | | `paymentMode` | Optional | Price against a specific payment method. Quotes can differ by method | | `partnerPctFee` / `partnerFixedFee` | Optional | Per-request partner fee override (see below) | ## Response The response echoes the route and returns `srcAmount`, `dstAmount`, `exchangeRate` and `feeDetails`. If no quote can be produced, `message` explains why. ## Partner fee (markup) To take a margin on an order, pass your fee on the quote and order-creation requests. Both fields are optional and **override** the fee configured on your account for that request: | Field | Type | Description | | - | - | - | | `partnerPctFee` | number | Partner fee as a percentage of the order amount (e.g. `1.5` = 1.5%) | | `partnerFixedFee` | number | Fixed partner fee in the order's base currency | They apply to the quote (`/ext/ramp_order/quote`) and the checkout order (`/ext/ramp_order/checkout`). When omitted, the account-configured partner fee applies. Send the **same** values on both calls so the order matches the quote you showed the user. ## Fee breakdown (`feeDetails`) Quote and order responses return a `feeDetails` array. Each entry has a `type`, `fee`, `currency`, and `isInclusive`. The returned `type` values are: | `type` | Description | Where | | - | - | - | | `platform` | Aggregated platform fee (Rampnow service + processing, and — in the quote — your partner fee) | quote + order | | `network` | Blockchain network (gas) fee | quote + order | | `partner` | Your partner fee / markup, broken out as its own line | order only (`GET /ext/ramp_order/{uid}` + webhook) | **Reconciliation.** In the quote, the partner fee is folded into `platform`, so `total = platform + network`. On the order responses it is broken out: `platform` **excludes** the partner amount and a `partner` line is added, so `total = platform + network + partner`. The total is identical either way. Create the checkout order with the user's details # Onramp & Offramp Intent Overview Source: https://docs.rampnow.io/quickstart/ramp-intent/overview Create buy and sell orders from your backend and hand the user to Rampnow's hosted checkout An **intent** is an order your backend creates with the amount, asset and destination already fixed. The user can't change them; they only verify and complete the order on Rampnow's hosted pages, then return to your app. One API covers both directions. You create an intent with `POST /ext/ramp_order/checkout`, sending the order and the user's details, and set `orderType`: * `"buy"` for **onramp**: the user pays fiat and receives crypto in their wallet. * `"sell"` for **offramp**: the user sends crypto and receives fiat in their bank account. Rampnow returns a checkout link for the user. ## How it works You own the screens before checkout: asset selection, amount, quote and user details. Rampnow runs everything after that, using trusted third-party KYC, payment and payout providers. Rampnow runs email/OTP, KYC, payment capture and settlement. The user pays with fiat (card, bank transfer, Apple Pay, …) and gets crypto delivered to the destination wallet. Rampnow runs email/OTP, KYC, deposit collection, fiat payout and settlement. The user sends crypto to the deposit address shown on the hosted page and gets fiat paid out to their bank account.
Build the pre-checkout journey yourself and pre-fill everything Rampnow needs User details go on the order. Rampnow creates or matches the customer automatically KYC, payment, deposit and payout run on Rampnow's hosted pages, through third-party providers Webhooks on every order status change
## Integration steps The same five steps apply to buy and sell orders. Each step page has an **Onramp (buy)** / **Offramp (sell)** switch where the two differ. Discover which assets, chains, payment modes and countries are live. [Read more →](/quickstart/ramp-intent/supported-config) Price the order with `POST /ext/ramp_order/quote` and show the user what they get. [Read more →](/quickstart/ramp-intent/get-quote) Call `POST /ext/ramp_order/checkout` with the order and the user's details. Rampnow creates the user for you. [Read more →](/quickstart/ramp-intent/create-order) Send the user to the returned `redirectUrl` to finish verification and the payment or crypto deposit. [Read more →](/quickstart/ramp-intent/redirect-user) Get status updates by webhook, or poll the order by your `externalOrderUid`. [Read more →](/quickstart/ramp-intent/track-order) Every call is authenticated with your API key and an HMAC signature. Set this up first: [Authentication](/api-reference/authentication). The examples in this guide use the production host `https://api.rampnow.io`. While integrating, use a **sandbox** key against `https://api.sandbox.rampnow.io` (checkout pages are served from `https://app.sandbox.rampnow.io`). Keys are environment-scoped. See [Environments](/api-reference/authentication#environments). ## Sequence ```mermaid theme={null} sequenceDiagram participant User participant Partner as Your Platform participant Rampnow as Rampnow API User->>Partner: Chooses asset + amount Partner->>Rampnow: POST /ext/ramp_order/quote Rampnow-->>Partner: Quote (amounts, feeDetails) Partner->>User: Shows quote User->>Partner: Confirms Partner->>Rampnow: POST /ext/ramp_order/checkout (order + user) Rampnow-->>Partner: orderUid, userUid, redirectUrl Partner->>User: Redirect to redirectUrl Rampnow->>User: Email + OTP verification Rampnow->>User: KYC (only if the user isn't already verified) Rampnow->>User: Payment with the selected or chosen payment method Rampnow-->>User: Redirect back to returnUrl Rampnow-->>Partner: Webhook (order status) ``` ```mermaid theme={null} sequenceDiagram participant User participant Partner as Your Platform participant Rampnow as Rampnow API User->>Partner: Chooses asset + amount Partner->>Rampnow: POST /ext/ramp_order/quote Rampnow-->>Partner: Quote (amounts, feeDetails) Partner->>User: Shows quote User->>Partner: Confirms Partner->>Rampnow: POST /ext/ramp_order/checkout (order + user) Rampnow-->>Partner: orderUid, userUid, redirectUrl Partner->>User: Redirect to redirectUrl Rampnow->>User: Email + OTP verification Rampnow->>User: KYC (only if the user isn't already verified) Rampnow->>User: Payout account confirmation Rampnow->>User: Crypto deposit to the address shown, with on-chain confirmation Rampnow-->>User: Redirect back to returnUrl Rampnow-->>Partner: Webhook (order status) ``` ## When to use the Intent flow * Platforms that want to own the pre-checkout experience (quote, asset picker, user details) * Apps that already hold verified user data and want to pre-fill it * Backends that need to create and reconcile orders server-to-server * Needs backend work: request signing, quote and order calls, webhook handling * The user leaves your app for the hosted checkout step * For a no-code embed, use the [Widget](/quickstart/ramp-widget/overview) or the [SDK](/quickstart/ramp-sdk/overview) instead Find out which buy and sell routes are live for your account # Step 4: Redirect the User Source: https://docs.rampnow.io/quickstart/ramp-intent/redirect-user Send the user to Rampnow's hosted checkout and bring them back Open the `redirectUrl` from Create Order in the user's browser. Rampnow takes over from there and sends the user back to your `returnUrl` when done. ## Open the checkout This works the same for buy and sell orders. ```javascript Same tab theme={null} window.location.href = redirectUrl; ``` ```javascript New tab theme={null} window.open(redirectUrl, "_blank", "noopener"); ``` On mobile, open `redirectUrl` in the system browser or an in-app browser tab (SFSafariViewController / Custom Tabs) rather than a bare WebView. KYC needs camera access. ## What the user does on the hosted page 1. Email + OTP verification 2. KYC (only if the user isn't already verified) 3. Payment with the selected or chosen payment method Details the user already gave you (name, email, wallet address, …) are pre-filled from the order. **After checkout:** Rampnow settles the payment and sends the crypto to the destination wallet. The order moves through its statuses and you receive a webhook on each change. 1. Email + OTP verification 2. KYC (only if the user isn't already verified) 3. Payout account confirmation 4. Crypto deposit to the address shown, with on-chain confirmation Details the user already gave you (name, email, bank account, …) are pre-filled from the order. **After checkout:** once the deposit is confirmed on-chain, Rampnow converts it to fiat and pays out to the bank account. You receive a webhook on each status change. ## When the user comes back When the user lands on your `returnUrl`, **don't treat the redirect as proof of success**. Confirm the final state from the webhook or by polling the order (next step). For a step-by-step view of what the user sees, see the [User Flow](/quickstart/ramp/flow). Follow the order to completion with webhooks or polling # Step 1: Check Supported Config Source: https://docs.rampnow.io/quickstart/ramp-intent/supported-config Discover which assets, networks, payment modes and countries are live for onramp and offramp Optional but recommended. Use these endpoints to build your asset and country pickers so users only see buy and sell routes that will succeed. ## Configuration endpoints Instead of probing `asset_config` pair-by-pair, use these bulk endpoints to discover what is live: | Endpoint | Returns | | - | - | | [`GET /api/partner/v1/ext/ramp_order/config`](/api-reference/configuration/get-order-config) | Full support matrix in one call: every configured asset, payment mode and order type, split by direction (`buy` = onramp, `sell` = offramp). | | [`GET /api/partner/v1/ext/ramp_order/blocked_currencies`](/api-reference/configuration/get-blocked-currencies) | Supported countries with the crypto currencies blocked in each. Pass `?country=` to filter to one country. | For narrower lookups, the [Configuration](/api-reference/configuration/get-asset-config) group also exposes `asset_config`, `chain_config`, `token_config` and `payment_mode_config`. All configuration endpoints are HMAC-authenticated like the rest of the external API. In the `config` response, read the **`buy`** direction for onramp and the **`sell`** direction for offramp. Cache the result and refresh it periodically. It changes rarely. Price the order before you create it # Step 5: Track the Order Source: https://docs.rampnow.io/quickstart/ramp-intent/track-order Follow the order to completion with webhooks or polling Use webhooks as the source of truth for order status. Polling is a fallback, e.g. when the user lands on your `returnUrl` before the webhook arrives. ## Webhooks (recommended) Rampnow sends a webhook on every order status change. Configure the endpoint in the API Hub of your **Rampnow Partner Dashboard**. Payload and signature verification are in the [Webhook Guide](/api-reference/webhook); the meaning of each status and what to do is on [Order Status](/api-reference/order-status). ## Polling To poll instead, use [Get Order](/api-reference/onramp-&-offramp-intent/get-an-order-by-external-id): ```bash cURL theme={null} curl --request GET \ --url https://api.rampnow.io/api/partner/v1/ext/ramp_order/your-unique-order-id \ --header 'X-RAMPNOW-API-KEY: YOUR_API_KEY' \ --header 'X-RAMPNOW-TIMESTAMP: UNIX_SECONDS' \ --header 'X-RAMPNOW-SIGN: HMAC_SHA256_SIGNATURE' ``` * The path parameter is **your** `externalOrderUid` (the id you sent on Create Order), not Rampnow's `orderUid`. * The response is an **array**, `orderList`, of orders matching that external id: normally one, empty if none. Read `status` (and `paymentStatus`) from the entry. See [Order Status](/api-reference/order-status) for every value. * Right after Create Order, the status is `unknown` until the user confirms on the hosted page. * On order responses, `feeDetails` includes a separate `partner` line. See [Fee breakdown](#fee-breakdown-feedetails) below. ## Fee breakdown (`feeDetails`) Quote and order responses return a `feeDetails` array. Each entry has a `type`, `fee`, `currency`, and `isInclusive`. The returned `type` values are: | `type` | Description | Where | | - | - | - | | `platform` | Aggregated platform fee (Rampnow service + processing, and — in the quote — your partner fee) | quote + order | | `network` | Blockchain network (gas) fee | quote + order | | `partner` | Your partner fee / markup, broken out as its own line | order only (`GET /ext/ramp_order/{uid}` + webhook) | **Reconciliation.** In the quote, the partner fee is folded into `platform`, so `total = platform + network`. On the order responses it is broken out: `platform` **excludes** the partner amount and a `partner` line is added, so `total = platform + network + partner`. The total is identical either way. Your Intent integration is complete for onramp and offramp. Review the order statuses to handle every outcome. # SDK Configuration Source: https://docs.rampnow.io/quickstart/ramp-sdk/configuration Every option you can pass to new RampnowSdk(), useRampnowSdk() and createRedirectUrl() Pass a config object to `new RampnowSdk(config)`, `useRampnowSdk(config)` or `RampnowSdk.createRedirectUrl(config)`. The SDK sends each order and display option to the widget as a URL parameter of the same name, so these are the same parameters the [Widget](/quickstart/ramp-widget/parameters) takes. ```javascript theme={null} const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', widgetUrl: 'https://app.sandbox.rampnow.io', orderType: 'buy', srcCurrency: 'EUR', srcChain: 'fiat', srcAmount: '250', dstCurrency: 'ETH', dstChain: 'ethereum', paymentMode: 'card', walletAddress: '0xUserWalletAddress', lockFields: ['dstAsset'], externalOrderId: 'your-order-1001', }) ``` ## SDK options These options control the SDK itself and are not sent to the widget: | Option | Type | Default | Description | | - | - | - | - | | `widgetUrl` | `string` | `https://app.rampnow.io` | Widget host. Set `https://app.sandbox.rampnow.io` for sandbox. | | `containerId` | `string` | — | DOM element ID for [embedded mode](/quickstart/ramp-sdk/display-modes#embedded-mode). Omit for an overlay. | | `width` | `string \| number` | `450` | Iframe width (px) | | `height` | `string \| number` | `700` | Iframe height (px) | `apiKey` is required: the constructor throws `[RampnowSdk] apiKey is required` without it. ## Order and display options In the SDK, `lockFields` is an array (`['srcAsset', 'dstAsset']`) and boolean options take `true`. You don't need to set `prefill`; the SDK adds it whenever `paymentMode` is set. ### Order details | Parameter | Type | Description | | - | - | - | | `apiKey` | string | **Required.** Your public partner key. Without it the order isn't attributed to your account. | | `orderType` | `buy` \| `sell` | Order direction. Defaults to `buy`. | | `srcCurrency` | string | Currency the user spends: fiat for `buy` (e.g. `EUR`), crypto for `sell` (e.g. `USDT`). Uppercase code. Needs `srcChain`. | | `srcChain` | string | Chain of `srcCurrency`. Use `fiat` for fiat currencies, otherwise a chain code such as `ethereum`, `bitcoin` or `arbitrum-one`. Needs `srcCurrency`. | | `dstCurrency` | string | Currency the user receives: crypto for `buy` (e.g. `ETH`), fiat for `sell` (e.g. `EUR`). Uppercase code. Needs `dstChain`. | | `dstChain` | string | Chain of `dstCurrency`. `fiat` for fiat currencies. Needs `dstCurrency`. | | `srcAmount` | string | Amount in the source currency, e.g. `250`. | | `paymentMode` | string | Payment method for `buy`, payout method for `sell`. Lowercase, e.g. `card`, `apple_pay`, `google_pay`, `sepa`, `open_banking`, `pix`, `upi`, `ach`. | | `walletAddress` | string | The user's wallet address for `buy`. Pre-selected or pre-filled once the user signs in. | | `walletAddressTag` | string | Memo or tag for chains that need one (XRP, XLM, etc.). | | `externalOrderId` | string | Your own order ID. Stored on the order and returned as `externalOrderUid` in [webhooks](/api-reference/webhook). | **Currencies only take effect with their chain.** `srcCurrency` is ignored unless `srcChain` is also set, and `dstCurrency` is ignored unless `dstChain` is also set. For example, pre-select EUR with `srcCurrency=EUR&srcChain=fiat`, not `srcCurrency=EUR` alone. **Defaults.** Every parameter except `apiKey` is optional, and a missing or invalid value never shows an error to the user. Rampnow fills the gaps from your account's live configuration: the first listed asset for the direction (usually the user's local fiat for `buy`), a typical amount for that currency, and the first active payment or payout method for the asset. Pass the values you care about rather than relying on these. Valid currency and chain codes, and the payment methods enabled for your account, are listed under [What's Supported](/quickstart/resources/supported-assets). Values are case-sensitive: `paymentMode=SEPA` is ignored, `paymentMode=sepa` works. ### Display options | Parameter | Type | Description | | - | - | - | | `prefill` | `true` | Shows the `paymentMode` you passed as already selected. Without it, the user picks the method themselves. The SDK sets this for you whenever `paymentMode` is set. | | `lockFields` | comma-separated list | Makes fields read-only: `srcAsset`, `srcAmount`, `dstAsset`, `paymentMode`, `walletAddress`. Locking doesn't set a value, so pass the matching parameter too (e.g. `dstCurrency` + `dstChain` for `dstAsset`). `paymentMode` and `walletAddress` locks are ignored without a value. | | `hideOrderTabs` | `true` | Hides the Buy/Sell tabs. Use with `orderType` to pin the widget to one direction. | | `hideMenu` | `true` | Hides the menu button in the widget header, so users stay in the order flow. | ### Attribution | Parameter | Type | Description | | - | - | - | | `utm_source` | string | UTM source, stored on the user and the order | | `utm_medium` | string | UTM medium | | `utm_campaign` | string | UTM campaign | ### `skipPaymentInstructions` SDK option, `boolean`. On a sell order, skips the crypto transfer instructions screen and shows a waiting screen instead. Use it when your wallet sends the crypto itself after the `ORDER_CREATED` [event](/quickstart/ramp-sdk/events), then pass the hash with the `UPDATE_PAYMENT_DETAIL` [command](/quickstart/ramp-sdk/events#commands). Overlay, embedded or redirect # SDK Display Modes Source: https://docs.rampnow.io/quickstart/ramp-sdk/display-modes Open the Rampnow widget as an overlay, embedded in your layout, or as a redirect URL The SDK opens the same widget in three ways. Pick one per entry point; you can use different modes in different parts of your app. | Mode | How | Best for | | - | - | - | | [Overlay](#overlay-mode) | `init()` without `containerId` | The quickest setup, a "Buy crypto" button anywhere | | [Embedded](#embedded-mode) | `init()` with `containerId` | A dedicated buy or sell page inside your layout | | [Redirect](#redirect-mode) | `RampnowSdk.createRedirectUrl()` | Native apps, email links, or opening in a new tab | ## Overlay mode Opens the widget as a full-screen modal overlay. This is the default when you don't pass `containerId`. ```javascript theme={null} import { RampnowSdk } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', orderType: 'buy', srcCurrency: 'EUR', dstCurrency: 'ETH', dstChain: 'ethereum', }) sdk.init() // Opens the overlay ``` Call `sdk.close()` to remove it, or let the user close it from the widget. ## Embedded mode Mounts the widget inside a DOM element you own. Pass the element's ID as `containerId`, and size it with `width` and `height`. ```html theme={null}
``` ```javascript theme={null} import { RampnowSdk } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', containerId: 'rampnow-widget', width: 450, height: 700, orderType: 'buy', dstCurrency: 'ETH', dstChain: 'ethereum', }) sdk.init() ``` ## Redirect mode Builds a widget URL without opening an iframe. Use it for native apps, email links or server-side flows. `createRedirectUrl` is static, so you don't need an instance. ```javascript theme={null} import { RampnowSdk } from '@rampnow/sdk' const url = RampnowSdk.createRedirectUrl({ apiKey: 'pk_YOUR_API_KEY', orderType: 'buy', srcCurrency: 'EUR', srcAmount: '250', dstCurrency: 'BTC', dstChain: 'bitcoin', walletAddress: 'bc1qYourWalletAddress', }) window.open(url, '_blank') ``` In redirect mode the widget runs outside your page, so [browser events](/quickstart/ramp-sdk/events) don't reach your app. Track the order with [webhooks](/api-reference/webhook). React to order progress and send data back to the widget # SDK Events & Commands Source: https://docs.rampnow.io/quickstart/ramp-sdk/events Subscribe to widget events and send commands to the widget with the Rampnow SDK The widget and your page talk in two directions: * **Events** go from the widget to your app. Subscribe with `sdk.on()`. * **Commands** go from your app to the widget. Send them with `sdk.send()`. Events are for updating your UI. Don't fulfil or credit anything on a browser event alone: confirm the order on your backend with [webhooks](/api-reference/webhook), which are signed and retried. ## Subscribing `sdk.on(eventType, callback)` returns the instance, so calls chain. `sdk.off(eventType, callback)` unsubscribes and takes the same forms. ```javascript theme={null} // Single event sdk.on(RampnowEventType.ORDER_CREATED, callback) // Multiple events sdk.on([RampnowEventType.ORDER_CREATED, RampnowEventType.ORDER_COMPLETED], callback) // All events sdk.on('*', callback) ``` ## Events Use the `RampnowEventType` enum: | Event | Payload | Description | | - | - | - | | `WIDGET_READY` | — | Widget iframe loaded and ready | | `WIDGET_CLOSED` | — | Widget was closed | | `USER_AUTHENTICATED` | — | User signed in | | `ORDER_CREATED` | `orderUid`, `orderType`, `srcCurrency`, `srcChain`, `srcAmount`, `dstCurrency`, `dstChain`, `paymentMode`, `cryptoTxnInfo?`, `bankTxnInfo?` | Order successfully created | | `ORDER_PAYMENT_PROCESSING` | `orderUid`, `status`, `paymentStatus` | Payment is being processed | | `ORDER_PAYMENT_COMPLETED` | `orderUid`, `status`, `paymentStatus` | Payment received by Rampnow | | `ORDER_PAYMENT_FAILED` | `orderUid`, `status`, `paymentStatus` | Payment failed | | `ORDER_COMPLETED` | `orderUid`, `status`, `srcCurrency`, `srcChain`, `srcAmount`, `dstCurrency`, `dstChain`, `dstAmount`, `walletAddress`, `transactionHash`, `cryptoTxnInfo?` | Order fully completed, crypto sent | | `ORDER_FAILED` | `orderUid`, `status` | Order failed or cancelled | | `KYC_STARTED` | — | User started identity verification | | `KYC_SUBMITTED` | — | User submitted KYC documents | | `KYC_APPROVED` | — | KYC approved | | `KYC_REJECTED` | — | KYC rejected | | `ERROR` | `message`, `code?` | Error occurred in the widget | `status` and `paymentStatus` use the same values as the API and webhooks. See [Order Status](/api-reference/order-status). ### `CryptoTxnInfo` Available in `ORDER_CREATED` and `ORDER_COMPLETED` payloads: | Field | Type | Description | | - | - | - | | `currency` | `string` | Crypto currency code, e.g. `"USDC"` | | `chain` | `string` | Chain code, e.g. `"ethereum"` | | `amount` | `string` | Amount credited to the receiver | | `receiverAddress` | `string` | Address that received the crypto | | `fillAmount` | `string` | Filled/settled amount (may differ from amount on partial fills) | | `senderAddress` | `string` | Address that sent the crypto | | `transactionHash` | `string` | On-chain transaction hash | | `status` | `string` | Status of the transaction | ### Example: full event handling ```javascript theme={null} import { RampnowSdk, RampnowEventType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', orderType: 'buy', dstCurrency: 'ETH', dstChain: 'ethereum', }) sdk .on(RampnowEventType.WIDGET_READY, () => { console.log('Widget is ready') }) .on(RampnowEventType.ORDER_CREATED, ({ payload }) => { console.log('Order created:', payload.orderUid) }) .on(RampnowEventType.ORDER_COMPLETED, ({ payload }) => { console.log('Order completed:', payload.orderUid) console.log('Tx hash:', payload.transactionHash) }) .on(RampnowEventType.ERROR, ({ payload }) => { console.error('Widget error:', payload.message) }) .init() ``` ## Commands `sdk.send(commandType, payload)` sends a command to the widget iframe and returns the instance. Use the `RampnowCommandType` enum. **Domain whitelisting required:** For security, the widget only accepts commands from whitelisted origins. To whitelist your domain, [contact Rampnow](https://rampnow.io/contact-us). Commands sent from non-whitelisted origins are silently ignored. | Command | Payload | Description | | - | - | - | | `UPDATE_PAYMENT_DETAIL` | `{ transactionHash }` | Update payment detail with a crypto transaction hash | ### Example: submit a transaction hash from your wallet On a sell order, if your wallet sends the crypto for the user, pass the hash to the widget so it can match the transfer: ```javascript theme={null} import { RampnowSdk, RampnowEventType, RampnowCommandType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY' }) // After your wallet completes a crypto transfer, send the hash to the widget sdk.send(RampnowCommandType.UPDATE_PAYMENT_DETAIL, { transactionHash: '0xabc123...', }) // Listen for confirmation that the payment is being processed sdk.on(RampnowEventType.ORDER_PAYMENT_PROCESSING, ({ payload }) => { console.log('Payment updated:', payload.paymentStatus) }) ``` ## Next steps
Confirm order status on your backend Run every order outcome before going live
# SDK Installation & Setup Source: https://docs.rampnow.io/quickstart/ramp-sdk/installation Install @rampnow/sdk, point it at sandbox or production, and open your first widget session ## Install ```bash npm theme={null} npm install @rampnow/sdk ``` ```bash yarn theme={null} yarn add @rampnow/sdk ``` ```bash pnpm theme={null} pnpm add @rampnow/sdk ``` **CDN (no bundler):** ```html theme={null} ``` When loaded from the CDN, the SDK is available as `window.RampnowSdk`. ## Environments The SDK opens production by default. Set `widgetUrl` to the sandbox host while you build and test. | Environment | Widget host | Key | | - | - | - | | Sandbox | `https://app.sandbox.rampnow.io` | Sandbox public key | | Production | `https://app.rampnow.io` | Production public key | Build and test against sandbox first. No real money or mainnet crypto moves there. See [Sandbox Testing](/api-reference/sandbox-testing) for test payments and testnet tokens. ## Open your first session ```javascript theme={null} import { RampnowSdk, RampnowEventType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', widgetUrl: 'https://app.sandbox.rampnow.io', orderType: 'buy', srcCurrency: 'EUR', srcChain: 'fiat', dstCurrency: 'ETH', dstChain: 'ethereum', }) ``` ```javascript theme={null} import { RampnowSdk, RampnowEventType } from '@rampnow/sdk' const sdk = new RampnowSdk({ apiKey: 'pk_YOUR_API_KEY', widgetUrl: 'https://app.sandbox.rampnow.io', orderType: 'sell', srcCurrency: 'USDT', srcChain: 'ethereum', dstCurrency: 'EUR', dstChain: 'fiat', }) ``` Every option is listed in [Configuration](/quickstart/ramp-sdk/configuration). ```javascript theme={null} sdk.on(RampnowEventType.ORDER_COMPLETED, (e) => { console.log('Order completed:', e.payload.orderUid) }) ``` See [Events & commands](/quickstart/ramp-sdk/events) for the full list. ```javascript theme={null} sdk.init() ``` Without `containerId` this opens a full-screen overlay. See [Display modes](/quickstart/ramp-sdk/display-modes) for embedded and redirect. ## React The SDK ships with a React hook. React (`>=18`) and ReactDOM (`>=18`) are optional peer dependencies, only required when you use the hook. ```tsx theme={null} import { useRampnowSdk } from '@rampnow/sdk/react' import { RampnowEventType } from '@rampnow/sdk' import { useEffect } from 'react' function BuyCryptoButton() { const { init, close, send, on, isOpen } = useRampnowSdk({ apiKey: 'pk_YOUR_API_KEY', srcCurrency: 'EUR', dstCurrency: 'ETH', }) useEffect(() => { on(RampnowEventType.ORDER_COMPLETED, (e) => { console.log('Completed:', e.payload.orderUid) close() }) }, [on, close]) return } ``` The hook returns the same `init`, `close`, `send`, `on` and `isOpen` as an SDK instance. ## Methods ### `new RampnowSdk(config)` Creates an SDK instance. Validates the `apiKey` and merges defaults. Does **not** open the widget; call `.init()` to display it. ### `sdk.init(): RampnowSdk` Opens the widget. If `containerId` is set, mounts the iframe in that element; otherwise creates a full-screen overlay. Returns `this` for chaining. ### `sdk.close(): void` Closes and removes the widget from the DOM. ### `sdk.on(eventType, callback): RampnowSdk` / `sdk.off(eventType, callback): RampnowSdk` Subscribe to and unsubscribe from widget events. See [Events](/quickstart/ramp-sdk/events#events). ### `sdk.send(commandType, payload): RampnowSdk` Send a command to the widget iframe. See [Commands](/quickstart/ramp-sdk/events#commands). ### `sdk.isOpen: boolean` Read-only. Whether the widget is currently displayed. ### `RampnowSdk.createRedirectUrl(config): string` (static) Builds a full widget URL from config without opening an iframe. See [Redirect mode](/quickstart/ramp-sdk/display-modes#redirect-mode). Every option you can pass to the SDK # Onramp & Offramp SDK Source: https://docs.rampnow.io/quickstart/ramp-sdk/overview Embed the Rampnow onramp and offramp widget from JavaScript or TypeScript, with events and a React hook The `@rampnow/sdk` package opens the Rampnow widget inside your web app with a few lines of code. It is a typed wrapper around the [Widget](/quickstart/ramp-widget/overview): the same buy and sell flow, the same parameters, plus browser events, programmatic open and close, and a React hook.
Full-screen modal overlay. Quickest setup, no container needed Mount the widget inside any DOM element in your layout Generate a URL to open in a new tab, for native apps or email links First-class React support via `useRampnowSdk`
Try the SDK live at [demo.rampnow.io](https://demo.rampnow.io/). ## How it works Pass your partner key and any order details you already know (direction, asset, amount, wallet address). See [Configuration](/quickstart/ramp-sdk/configuration). React in your UI as the order moves, for example when it is created or completed. See [Events & commands](/quickstart/ramp-sdk/events). Call `init()`. The user completes sign-in, KYC and payment inside the Rampnow widget. See [Display modes](/quickstart/ramp-sdk/display-modes). Browser events are for UI only. Use [webhooks](/api-reference/webhook) as the source of truth for order status. ## Your partner API key Every widget session, whether opened by URL, iframe or the SDK, carries your **partner API key** as `apiKey`. That key is what makes the order yours: * Orders placed through your integration are attributed to your account in the [Partner Dashboard](https://partner.rampnow.io) * Order status webhooks go to your endpoint * The partner fee agreed with Rampnow for your account applies to each order. It can be zero if you integrate for visibility only Use the **public key** from the Partner Dashboard under **API Hub**. Sandbox and production keys are different, and each only works against its own environment. The public key is safe to ship in client-side code. You don't need a backend, an API secret or request signing to open the widget. The API key and secret pair (with `X-RAMPNOW-SIGN`) is only for the [Onramp & Offramp Intent](/quickstart/ramp-intent/overview) REST API. ## When to use the SDK * Web apps that need event callbacks as the order moves * Programmatic control: open and close the widget from your own buttons * React apps, through the `useRampnowSdk` hook * Type-safe configuration with TypeScript * You only need a link or an iframe: the [Widget](/quickstart/ramp-widget/overview) needs no JavaScript * You want to build the pre-checkout journey yourself and create orders server-to-server: use [Onramp & Offramp Intent](/quickstart/ramp-intent/overview) See [Integrations Overview](/quickstart/integrations/overview) for a side-by-side comparison. Install the package and open your first widget session # Embed the Widget Source: https://docs.rampnow.io/quickstart/ramp-widget/embed Open the Rampnow widget in an iframe on your page, or redirect the user to it There are two ways to open the widget URL. Both run the same flow and take the same [parameters](/quickstart/ramp-widget/parameters). | Mode | The user | Best for | | - | - | - | | [iFrame](#iframe-mode) | Stays on your page | Web apps that want an in-page experience | | [Redirect](#redirect-mode) | Goes to Rampnow's hosted page | The simplest setup, native apps, email links | The examples below use the sandbox host. Switch to `https://app.rampnow.io` and your production key when you go live. ## iFrame mode Embed the widget directly on your page. Add a container element in your HTML: ```html theme={null}
```
```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=BTC&dstChain=bitcoin&walletAddress=bc1qYourWalletAddress ``` ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=sell&srcCurrency=USDT&srcChain=ethereum&srcAmount=500&dstCurrency=EUR&dstChain=fiat ``` Put an iframe pointing at that URL inside your container: ```html theme={null} ```
Keep `allow="payment"` on the iframe. Browser payment methods inside the widget need it. ## Redirect mode Send the user to Rampnow's hosted page. Build the URL exactly as for the iframe: ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=BTC&dstChain=bitcoin&walletAddress=bc1qYourWalletAddress ``` ```javascript theme={null} window.location.href = "https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=BTC&dstChain=bitcoin&walletAddress=bc1qYourWalletAddress"; ``` In this example, the user buys **250 EUR** worth of **BTC** on the Bitcoin chain, with the crypto sent to `bc1qYourWalletAddress`. ## Track the order Track orders with [webhooks](/api-reference/webhook), and pass `externalOrderId` to match each order to your own records. If you also want browser events in your page, such as `ORDER_COMPLETED`, use the [SDK](/quickstart/ramp-sdk/events) instead. ## Next steps
Set up server-side order notifications Run every order outcome before going live
# Onramp & Offramp Widget Source: https://docs.rampnow.io/quickstart/ramp-widget/overview Embed Rampnow's buy and sell flow in your product with a URL or an iframe The widget is Rampnow's complete buy and sell flow, opened with a URL. You link to it or put it in an iframe, pass your partner key and any order details you already know as query parameters, and Rampnow runs everything from sign-in to delivery. There is no package to install and no JavaScript to write.
Keep users on your page with an iframe, or send them to the hosted page Configure everything with URL parameters Every payment method enabled for your account, out of the box Pre-fill direction, assets, amount and wallet address
Need event callbacks, open/close control or a React hook? The [SDK](/quickstart/ramp-sdk/overview) wraps the widget with a JavaScript API and accepts the same parameters. ## How it works Start from the widget host for your environment, add `/order/quote` and your parameters. See [URL parameters](/quickstart/ramp-widget/parameters). Load the URL in an iframe or redirect the user to it. See [Embed](/quickstart/ramp-widget/embed). The user signs in, passes KYC if needed and pays or sends crypto. See [User Flow](/quickstart/ramp/flow) for what they see. Rampnow sends a webhook to your endpoint on every status change. See the [Webhook guide](/api-reference/webhook). ## Your partner API key Every widget session, whether opened by URL, iframe or the SDK, carries your **partner API key** as `apiKey`. That key is what makes the order yours: * Orders placed through your integration are attributed to your account in the [Partner Dashboard](https://partner.rampnow.io) * Order status webhooks go to your endpoint * The partner fee agreed with Rampnow for your account applies to each order. It can be zero if you integrate for visibility only Use the **public key** from the Partner Dashboard under **API Hub**. Sandbox and production keys are different, and each only works against its own environment. The public key is safe to ship in client-side code. You don't need a backend, an API secret or request signing to open the widget. The API key and secret pair (with `X-RAMPNOW-SIGN`) is only for the [Onramp & Offramp Intent](/quickstart/ramp-intent/overview) REST API. ## Environments | Environment | Widget host | Key | | - | - | - | | Sandbox | `https://app.sandbox.rampnow.io` | Sandbox public key | | Production | `https://app.rampnow.io` | Production public key | Build and test against sandbox first. No real money or mainnet crypto moves there. See [Sandbox Testing](/api-reference/sandbox-testing) for test payments and testnet tokens. ## When to use the widget * Platforms that want a deeply integrated experience with minimal code * The fastest launch: a link or an iframe is enough * Native apps and emails, where a URL is easier than a JavaScript SDK * Teams that want Rampnow's pre-built UI, updated without code changes * You need browser events or programmatic control: use the [SDK](/quickstart/ramp-sdk/overview) * You want to build the pre-checkout journey yourself and create orders server-to-server: use [Onramp & Offramp Intent](/quickstart/ramp-intent/overview) See [Integrations Overview](/quickstart/integrations/overview) for a side-by-side comparison. Every parameter you can pass to the widget # Widget URL Parameters Source: https://docs.rampnow.io/quickstart/ramp-widget/parameters Every query parameter you can pass to the Rampnow widget URL Configure the widget with query parameters on the `/order/quote` path of the [widget host](/quickstart/ramp-widget/overview#environments): ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=ETH&dstChain=ethereum&walletAddress=0xUserWalletAddress ``` Link to `/order/quote`, not the bare host. Opening the host root redirects to the quote page and drops your query parameters. ### Order details | Parameter | Type | Description | | - | - | - | | `apiKey` | string | **Required.** Your public partner key. Without it the order isn't attributed to your account. | | `orderType` | `buy` \| `sell` | Order direction. Defaults to `buy`. | | `srcCurrency` | string | Currency the user spends: fiat for `buy` (e.g. `EUR`), crypto for `sell` (e.g. `USDT`). Uppercase code. Needs `srcChain`. | | `srcChain` | string | Chain of `srcCurrency`. Use `fiat` for fiat currencies, otherwise a chain code such as `ethereum`, `bitcoin` or `arbitrum-one`. Needs `srcCurrency`. | | `dstCurrency` | string | Currency the user receives: crypto for `buy` (e.g. `ETH`), fiat for `sell` (e.g. `EUR`). Uppercase code. Needs `dstChain`. | | `dstChain` | string | Chain of `dstCurrency`. `fiat` for fiat currencies. Needs `dstCurrency`. | | `srcAmount` | string | Amount in the source currency, e.g. `250`. | | `paymentMode` | string | Payment method for `buy`, payout method for `sell`. Lowercase, e.g. `card`, `apple_pay`, `google_pay`, `sepa`, `open_banking`, `pix`, `upi`, `ach`. | | `walletAddress` | string | The user's wallet address for `buy`. Pre-selected or pre-filled once the user signs in. | | `walletAddressTag` | string | Memo or tag for chains that need one (XRP, XLM, etc.). | | `externalOrderId` | string | Your own order ID. Stored on the order and returned as `externalOrderUid` in [webhooks](/api-reference/webhook). | **Currencies only take effect with their chain.** `srcCurrency` is ignored unless `srcChain` is also set, and `dstCurrency` is ignored unless `dstChain` is also set. For example, pre-select EUR with `srcCurrency=EUR&srcChain=fiat`, not `srcCurrency=EUR` alone. **Defaults.** Every parameter except `apiKey` is optional, and a missing or invalid value never shows an error to the user. Rampnow fills the gaps from your account's live configuration: the first listed asset for the direction (usually the user's local fiat for `buy`), a typical amount for that currency, and the first active payment or payout method for the asset. Pass the values you care about rather than relying on these. Valid currency and chain codes, and the payment methods enabled for your account, are listed under [What's Supported](/quickstart/resources/supported-assets). Values are case-sensitive: `paymentMode=SEPA` is ignored, `paymentMode=sepa` works. ### Display options | Parameter | Type | Description | | - | - | - | | `prefill` | `true` | Shows the `paymentMode` you passed as already selected. Without it, the user picks the method themselves. The SDK sets this for you whenever `paymentMode` is set. | | `lockFields` | comma-separated list | Makes fields read-only: `srcAsset`, `srcAmount`, `dstAsset`, `paymentMode`, `walletAddress`. Locking doesn't set a value, so pass the matching parameter too (e.g. `dstCurrency` + `dstChain` for `dstAsset`). `paymentMode` and `walletAddress` locks are ignored without a value. | | `hideOrderTabs` | `true` | Hides the Buy/Sell tabs. Use with `orderType` to pin the widget to one direction. | | `hideMenu` | `true` | Hides the menu button in the widget header, so users stay in the order flow. | ### Attribution | Parameter | Type | Description | | - | - | - | | `utm_source` | string | UTM source, stored on the user and the order | | `utm_medium` | string | UTM medium | | `utm_campaign` | string | UTM campaign | ## Examples Buy 250 EUR of ETH on Ethereum by card, with ETH locked and the wallet pre-filled: ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=buy&srcCurrency=EUR&srcChain=fiat&srcAmount=250&dstCurrency=ETH&dstChain=ethereum&paymentMode=card&prefill=true&walletAddress=0xUserWalletAddress&lockFields=dstAsset&hideOrderTabs=true&externalOrderId=your-order-1001 ``` Sell 500 USDT on Ethereum for EUR paid out by SEPA: ```text theme={null} https://app.sandbox.rampnow.io/order/quote?apiKey=YOUR_API_KEY&orderType=sell&srcCurrency=USDT&srcChain=ethereum&srcAmount=500&dstCurrency=EUR&dstChain=fiat&paymentMode=sepa&prefill=true&hideOrderTabs=true&externalOrderId=your-order-2001 ``` URL-encode values that can contain reserved characters, such as `externalOrderId`. Open the URL in an iframe or redirect the user to it # User Flow Source: https://docs.rampnow.io/quickstart/ramp/flow Step-by-step view of the buy (fiat to crypto) and sell (crypto to fiat) journeys your users go through This page walks through what your users see, from the quote to the moment they receive their crypto or fiat. The flow depends on `orderType`: `buy` for onramp, `sell` for offramp. Pick a direction below. Rampnow runs every step for you. Onramp flow overview The client requests a quote with purchase parameters. Quote generation interface **Request includes:** * Fiat currency * Payment method * Token & blockchain * Purchase amount **Rampnow returns:** * Total cost breakdown * Network fees * Payment processing fees * Token amount to receive Quotes are deterministic and guaranteed upon checkout User provides the destination wallet address. Wallet address input **Address validation checks:** * ✓ Correct blockchain network * ✓ Account Abstraction * ✓ Valid checksum format * ✓ Contract restrictions * ✓ Valid address format Users can manually enter an address or connect their wallet directly Security verification to prevent fraud and ensure account validity. Email and OTP verification **Verification process:** 1. User enters email address 2. OTP code sent to email 3. User submits OTP 4. Session validated and secured User completes payment using their preferred method. Payment method selection **Supported payment methods:** * **Cards**: Visa, Mastercard (instant processing) * **Bank transfers**: SEPA (EU), ACH (US), PIX (Brazil), UPI (India), SPEI (Mexico), Khipu (Chile) * **Digital wallets**: Apple Pay, Google Pay, Cash App (US), local APMs See [Payment Methods & Limits](/quickstart/resources/payment-methods) for the full, live list of active payment rails by country. **Status updates via:** * Real-time webhooks * API polling Funds are settled and crypto is prepared for delivery. Settlement and crypto delivery **Settlement process:** 1. Payment confirmation received 2. Funds settle to banking partners 3. Routing engine triggers liquidity movement 4. Crypto assets prepared for blockchain delivery Settlement times vary by payment method (instant to 1-3 business days) Tokens are delivered to the user's wallet address. Token delivery to wallet **Delivery process:** 1. Transaction broadcast to blockchain 2. Network confirmation 3. Transaction hash generated 4. Final delivery confirmation Transaction complete! Users can track their tokens on the blockchain explorer. Offramp flow overview The client requests a **sell quote** specifying the digital asset being sold and the target fiat currency. Sell quote page **Request parameters:** * Token & blockchain * Crypto amount * Desired payout currency * User region (auto-detected or provided) **Rampnow returns:** * Estimated fiat amount * FX and payout fees * Network fee * Estimated payout arrival time * Available payout methods Quotes are deterministic and guarantee the payout amount after asset confirmation. The user initiates a blockchain transfer to the **deposit address** generated for the order. Deposit address QR code **Rampnow monitors:** * Incoming transaction * Confirmations on the blockchain * Token matching & amount validation * Expiration handling if no funds are received **Status transitions:** * `created` → order created, awaiting the user's crypto deposit * `processing` → funds received and confirmed; conversion + payout in progress Each order has to be updated with transaction hash for accurate reconciliation. Rampnow sends fiat to the user's bank account. Payout screen **Supported payout rails:** * **Bank transfers**: SEPA (EU) * **Coming soon**: Visa Direct, mobile wallets, regional payout partners See [Payment Methods & Limits](/quickstart/resources/payment-methods#offramp-payout-timing) for payout currencies, rails and timings. Payout timing depends on the selected rail (instant → 1–2 business days). Once fiat is delivered, the order is completed and final confirmations are issued. **Final outputs:** * Transaction hash (crypto side) * Banking reference / trace ID (fiat side) * Final webhook: `ramp_order.status_change` with `payload.orderStatus = completed` Users can track blockchain delivery and payout references for transparency. *** ## Order statuses Buy and sell orders share one set of statuses, delivered on every change by [webhook](/api-reference/webhook) or by polling the order. A successful order goes `created` → `processing` → `completed`. See [Order Status](/api-reference/order-status) for every status, which ones are final, and what to do in each. ## Integration options
Open the widget from JavaScript, with events and a React hook Embed the complete buy and sell flow with a URL or an iframe Create orders from your backend and redirect to the hosted checkout
*** ## Next steps Open Rampnow through the [SDK](/quickstart/ramp-sdk/overview) or the [Widget](/quickstart/ramp-widget/overview), or create orders with [Intent](/quickstart/ramp-intent/overview). Set `orderType` to `buy` or `sell`. See [Integrations Overview](/quickstart/integrations/overview) to choose. Simulate successful, failed and expired orders with testnet tokens. See [Sandbox Testing](/api-reference/sandbox-testing). Receive real-time order status updates. See the [Webhook Guide](/api-reference/webhook). Check your integration against the [Go-Live Checklist](/api-reference/go-live-checklist). Follow the Widget quickstart, or the SDK quickstart in the same tab # Supported Blockchains Source: https://docs.rampnow.io/quickstart/resources/blockchain-list Networks Rampnow supports and the chain code to use for each This page lists the blockchain networks Rampnow supports and whether each one is open for onramp, offramp or both. For the tokens on each network see [Supported Crypto Currencies](/quickstart/resources/supported-assets). **Real-time data** — This list is generated directly from the live Rampnow configuration API and updates automatically as new blockchains are added or removed.
## Reading the list * **Chain code** is the exact value to pass as the chain in your integration (for example `srcChain` / `dstChain`). Codes are case-sensitive. * **Onramp** / **Offramp** show whether users can buy to, or sell from, that chain, with the number of tokens available in that direction. The tokens themselves are listed on [Supported Crypto Currencies](/quickstart/resources/supported-assets). * A chain appears only when at least one token on it has an active route. Chains that are paused or not yet enabled in production are hidden. ## Missing a chain? Email `admin@rampnow.io` with the chain name, chain ID, public RPC endpoints, the native token, and any token contract addresses you need. Timelines depend on technical complexity, liquidity and regulatory review. # Payment Methods & Limits Source: https://docs.rampnow.io/quickstart/resources/payment-methods How users pay for onramp orders and receive fiat from offramp orders, and the limits that apply This page covers the fiat side of an order: which currencies and payment methods users can pay with on onramp, how they receive fiat on offramp, and the limits that apply. For which countries users can be based in see [Supported Countries](/quickstart/resources/supported-countries); for the full list of fiat currencies see [Supported Fiat Currencies](/quickstart/resources/supported-fiat); for tokens see [Supported Crypto Currencies](/quickstart/resources/supported-assets). Onramp supports cards and wallets (Visa, Mastercard, Apple Pay, Google Pay), bank transfers and local payment methods, depending on the currency. Offramp pays out to a bank account. ## Payment methods **Real-time data** — These tables are generated directly from the live Rampnow configuration API. A currency and method appear only while they are active. How users can pay, per fiat currency. How users receive fiat, per payout currency. The code in brackets is the value to pass as `paymentMode`, for example `paymentMode=sepa`. Codes are case-sensitive. Exact availability also depends on the user's card issuer, 3DS/SCA requirements and region. Intent API partners can read the same data from `paymentModeConfigs` in [`GET /ext/ramp_order/config`](/api-reference/configuration/get-order-config). ## Offramp payout timing | Rail | Region | Currency | Speed | | - | - | - | - | | **SEPA Credit Transfer** | EU | EUR | Same day or T+1 | Timing depends on banking cut-off times, weekends and holidays. Card payouts (Visa Direct, Mastercard Send) are on the roadmap. On a sell order, the webhook's `walletAddress` is the payout IBAN and `transactionHash` holds the payout reference. See the [User Flow](/quickstart/ramp/flow) for how settlement works and the [Webhook Guide](/api-reference/webhook#payload) for the full payload. ## Limits Higher KYC verification unlocks higher per-transaction and cumulative (velocity) limits. These are the current caps by verification level, pulled live from the config API: Minimum and maximum amounts per single transaction, enforced at the payment processor level. Rolling day limit calculated from the current transaction backwards. Resets continuously as older transactions age out. Enterprise partners can request custom limit configurations based on their use case. ## Need custom limits or a new payment method? Email `admin@rampnow.io` with your use case, expected transaction volumes and target regions. # Supported Crypto Currencies Source: https://docs.rampnow.io/quickstart/resources/supported-assets Crypto tokens your users can buy (onramp) and sell (offramp) This page lists the crypto tokens available on Rampnow, per direction. For the networks themselves see [Supported Blockchains](/quickstart/resources/blockchain-list); for fiat currencies see [Supported Fiat Currencies](/quickstart/resources/supported-fiat). **Real-time data** — This list is generated directly from the live Rampnow configuration API and updates automatically as new assets are added or removed. ## Reading the list A token can be available to buy, to sell, or both. The **Onramp** tab lists what users can buy; the **Offramp** tab lists what they can sell. A token on several chains appears once per chain. For example, `USDT` has a separate row for `ethereum`, `arbitrum-one`, `polygon` and so on, and each row is enabled independently. Pass the **Symbol** as the currency and the **Chain** as the chain (for example `srcCurrency` / `srcChain` on a sell order). Codes are case-sensitive. Intent API partners can check availability programmatically: read `assetConfigs` from [`GET /ext/ramp_order/config`](/api-reference/configuration/get-order-config). Only tokens with an active route in that direction are shown. Internal test assets, disabled assets and temporarily paused routes are not listed. ## Missing a token? Email `admin@rampnow.io` or use the [token listing form](https://rampnow.io/en/list#list-token-form) with the token symbol and name, contract address, target chain, and your use case or timeline. Requests are reviewed based on demand and liquidity. # Supported Countries Source: https://docs.rampnow.io/quickstart/resources/supported-countries Which countries your users can be based in: supported, restricted and prohibited jurisdictions This page lists where your users can be based to use Rampnow, for both **onramp** and **offramp**. For the payment methods available in each currency, see [Payment Methods & Limits](/quickstart/resources/payment-methods). If a country is not listed under *Prohibited* or *Restricted*, it is considered **supported** for Rampnow services. ## How Country Classification Works
Fully eligible for all Rampnow services. Limited availability. Additional review may be required for onboarding and operations. No services available. Rampnow does not onboard customers or process payments from these jurisdictions.
## Country Status ### Fully Supported Countries These countries are fully eligible for Rampnow services. Andorra, Austria, Belgium, Bosnia and Herzegovina, Croatia, Cyprus, Czech Republic, Denmark, Estonia, Faroe Islands, Finland, France, Germany, Gibraltar, Greece, Greenland, Guernsey, Hungary, Iceland, Ireland, Isle of Man, Italy, Jersey, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Montenegro, Netherlands, Norway, Poland, Portugal, Romania, San Marino, Slovakia, Slovenia, Spain, Sweden, Switzerland, United Kingdom Armenia, Australia, Azerbaijan, Bahrain, Bhutan, Brunei, Fiji, Georgia, Hong Kong, India, Indonesia, Israel, Japan, Jordan, Kazakhstan, Kuwait, Kyrgyzstan, Macau, Malaysia, Micronesia, Mongolia, New Zealand, Oman, Papua New Guinea, Philippines, Qatar, Saudi Arabia, Singapore, South Korea, Sri Lanka, Taiwan, Thailand, Turkey, United Arab Emirates, Uzbekistan, Vietnam Argentina, Aruba, Bahamas, Barbados, Belize, Bermuda, Brazil, Chile, Colombia, Costa Rica, Curaçao, Dominican Republic, Ecuador, El Salvador, Guyana, Mexico, Panama, Paraguay, Peru, United States, Uruguay Kenya, Mauritius, Rwanda, South Africa, Zambia ### Restricted Jurisdictions Rampnow maintains **limited availability** for customers or partners based in these jurisdictions. Any onboarding, operational activity, or integration may require additional review and approval. Algeria, Angola, Benin, Burkina Faso, Cameroon, Chad, Congo (Republic of), Côte d'Ivoire, Djibouti, Egypt, Equatorial Guinea, Eritrea, Eswatini, Gabon, Gambia, Ghana, Guinea, Guinea-Bissau, Liberia, Madagascar, Mauritania, Morocco, Mozambique, Namibia, Niger, Nigeria, Senegal, Sierra Leone, Sudan, Tanzania, Togo, Tunisia, Uganda Bangladesh, Cambodia, China, Iraq, Kosovo, Nepal, Pakistan, Timor-Leste, Tokelau, Tonga, Tuvalu, Vanuatu Bolivia, Canada, French Guiana, Guatemala, Haiti, Honduras, Nicaragua, Trinidad and Tobago Bonaire Sint Eustatius and Saba, Bulgaria, Moldova, North Macedonia, Serbia, Ukraine If you're operating from a restricted country and need to integrate Rampnow, contact our team at **[admin@rampnow.io](mailto:admin@rampnow.io)** for approval. ### Prohibited Jurisdictions Rampnow **does not** onboard customers, process payments, or establish any business relationships with users or entities from the following jurisdictions, regardless of currency or payment method. This restriction applies to **all services** . These restrictions apply globally and without exception. **Complete list of prohibited countries:** * Afghanistan * American Samoa * Belarus * Burundi * Central African Republic * Congo (Democratic Republic) * Cuba * Crimea region of Ukraine * Donetsk People's Republic * Guam * Iran * North Korea * Lebanon * Libya * Luhansk People's Republic * Mali * Myanmar * Northern Mariana Islands * Puerto Rico * Russian Federation * Somalia * South Sudan * Syria * United States Minor Outlying Islands * U.S. Virgin Islands * Venezuela * Yemen * Zimbabwe This list is subject to change. ## Questions or expansion requests To confirm availability for a specific country, or to request expansion to a new jurisdiction, email `admin@rampnow.io` with the country and your use case. Country classifications and restrictions are subject to change. This page is updated regularly to reflect current availability. # Supported Fiat Currencies Source: https://docs.rampnow.io/quickstart/resources/supported-fiat Fiat currencies your users can pay with (onramp) and receive (offramp) This page lists the fiat currencies available on Rampnow, per direction. For how users pay or get paid in each currency, and the limits that apply, see [Payment Methods & Limits](/quickstart/resources/payment-methods); for crypto tokens see [Supported Crypto Currencies](/quickstart/resources/supported-assets). **Real-time data** — This list is generated directly from the live Rampnow configuration API and updates automatically as currencies are added or removed. Currencies your users can pay with. Currencies your users can receive. ## Reading the list A currency can be available to pay with, to receive, or both. The **Onramp** tab lists what users can pay with; the **Offramp** tab lists what they can receive. Pass the ISO 4217 **Code** as the fiat currency (for example `srcCurrency=EUR` on a buy order, `dstCurrency=EUR` on a sell order) with the chain `fiat`. Codes are case-sensitive. Intent API partners can check availability programmatically: read `assetConfigs` from [`GET /ext/ramp_order/config`](/api-reference/configuration/get-order-config), where fiat entries are keyed `:fiat`. A currency is listed only while it has at least one active payment method in that direction. Which methods are available also depends on the user's country; see [Payment Methods & Limits](/quickstart/resources/payment-methods). ## Missing a currency? Email `admin@rampnow.io` with the currency, the countries your users are in, and your expected volume. Requests are reviewed based on demand and payment-partner coverage.