# Complete Account Registration Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/complete-account-registration post /account/complete-registration Complete a pending account registration request. This is used when merchant registration type is set to open # Create Account Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/create-account post /account Create accounts. # Create Bank Account Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/create-bank-account put /account Create bank account. You need to complete KYC before creating a bank account. # Get Account Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/get-account get /account Get all accounts or filter using account name. The account name must be an alphanumeric lowercase hex string between 2 to 40 characters long # Get Account Permissions Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/get-account-permissions get /account/permissions Get the valid permissions for accounts. # Get user Account Profile Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/get-account-profile get /account/myprofile Get user account profile. # Get Accounts Details Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/get-accounts-details get /account/account-details Get accounts details. # Register Account Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/register-account post /account/register Create a pending account registration request. This is used when merchant registration type is set to open # Search Account Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/search-account get /account/search Search matching account names using at least 2 characters # Update Account Email Source: https://docs.getpartna.com/api-reference/endpoint/v3/account/update-account-email put /account/email Update account email. # Add IP to Whitelist Source: https://docs.getpartna.com/api-reference/endpoint/v3/admin/add-ip-to-whitelist post /user/ip-whitelist Add ip address(es) to the whitelist. # Get Whitelisted IPs Source: https://docs.getpartna.com/api-reference/endpoint/v3/admin/get-whitelisted-ips get /user/ip-whitelist Retrieve all whitelisted ip addresses for a merchant. # Remove IP from Whitelist Source: https://docs.getpartna.com/api-reference/endpoint/v3/admin/remove-ip-from-whitelist delete /user/ip-whitelist Remove ip address from the whitelist. # Get Banks Source: https://docs.getpartna.com/api-reference/endpoint/v3/bank/get-banks get /banks Get supported banks by currency. # Get Discounts Source: https://docs.getpartna.com/api-reference/endpoint/v3/discount/get-discounts get /discount Get discounts applied on USD-NGN offramp transaction rates based on the transaction amount. # Check for manual kyc Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/check-for-manual-kyc get /user/kyc-exempt Checks if the merchant is exempt from KYC requirements. # Confirm KYC OTP Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/confirm-kyc-otp put /kyc/confirm-otp Confirm KYC verification OTP. # Confirm Phone Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/confirm-phone put /kyc/confirm-phone Confirm phone number associated with account BVN. # Handle BVN OTP Method Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/handle-bvn-otp-method-1 put /kyc/verification-method Choose BVN verification method. # Initiate BVN KYC Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/initiate-bvn-kyc post /kyc Initiate account KYC with BVN. # Initiate user's phone number validation by country Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/initiate-users-phone-number-validation-by-country post /phone Initiate user's phone number validation by country # Merchant KYC users manually Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/merchant-kyc-users-manually post /kyc/complete-kyc Allows a merchant to manually populate KYC information for their members. # Populate address details for an existing NG KYC record Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/populate-address-details-for-an-existing-ng-kyc-record put /kyc/address Adds street address, city, and state to the other details for an existing NG KYC record. # Resolve Bank Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/resolve-bank post /kyc/resolve-account Resolve bank account by account number and bank code. # Supported Verification Methods by country Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/supported-verification-methods-by-country get /kyc/supported-methods Get supported KYC verification methods by country. # Used to get a user's verified phone numbers by country Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/used-to-get-a-users-verified-phone-numbers-by-country get /phone Used to get a user's verified phone numbers by country. # Validate a user's phone number Source: https://docs.getpartna.com/api-reference/endpoint/v3/kyc/validate-a-users-phone-number put /phone/verify Validate a user's phone number # Disable Account MFA Source: https://docs.getpartna.com/api-reference/endpoint/v3/mfa/disable-account-mfa put /mfa/disable Disable account MFA. # Enable Account MFA Source: https://docs.getpartna.com/api-reference/endpoint/v3/mfa/enable-account-mfa post /mfa Initiate account MFA enrollment. # Get Account MFA Source: https://docs.getpartna.com/api-reference/endpoint/v3/mfa/get-account-mfa get /mfa Get account MFA details. # Validate Account MFA Source: https://docs.getpartna.com/api-reference/endpoint/v3/mfa/validate-account-mfa put /mfa Complete account MFA enrollment. # Mock Deposit Source: https://docs.getpartna.com/api-reference/endpoint/v3/mock/mock-deposit post /mock/deposit Mock crypto and fiat deposit. # Mock Fiat Deposit Source: https://docs.getpartna.com/api-reference/endpoint/v3/mock/mock-fiat-deposit post /mock/deposit-fiat Mock fiat deposit. # Get Pending Pricing Source: https://docs.getpartna.com/api-reference/endpoint/v3/pricing/get-pending-pricing get /pricing/pending Get pending pricing. # Pricing Alert Source: https://docs.getpartna.com/api-reference/endpoint/v3/pricing/pricing-alert get /pricing/trigger Trigger pricing reminder, charge, retry, receipt or control-account reconciliation. # Calculate ramp fees from toAmount or fromAmount Source: https://docs.getpartna.com/api-reference/endpoint/v3/ramp/calculate-ramp-fees-from-toamount-or-fromamount post /ramp/breakdown Calculate ramp fees from toAmount or fromAmount # Get Ramp Requests Source: https://docs.getpartna.com/api-reference/endpoint/v3/ramp/get-ramp-requests get /ramp Gets ramp requests. # Handle Ramp Source: https://docs.getpartna.com/api-reference/endpoint/v3/ramp/handle-ramp post /ramp/complete Manual handler for completing a ramp request. # Onramp and Offramp Source: https://docs.getpartna.com/api-reference/endpoint/v3/ramp/onramp-and-offramp post /ramp Initiates onramp and offramp requests. # Create a custom ratekey for conversion Source: https://docs.getpartna.com/api-reference/endpoint/v3/rate/create-a-custom-ratekey-for-conversion post /convert/preorder Create a custom rate key to complete a conversion transaction. # Get Rate Source: https://docs.getpartna.com/api-reference/endpoint/v3/rate/get-rate get /rate Get Rate details. # Verify Rate Key Source: https://docs.getpartna.com/api-reference/endpoint/v3/rate/verify-rate-key get /rate/verify-rate-key Verify Rate Key. # Get Supported Assets Source: https://docs.getpartna.com/api-reference/endpoint/v3/supported/get-supported-assets get /supported/assets Get a list of supported assets including symbols, contract details, withdrawal constraints, and transaction explorer links. # Convert Currency Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/convert-currency post /convert Convert between currencies. # Create Transfer Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/create-transfer post /transfer Create transfer. # Get Single Transaction Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/get-single-transaction get /transfer/details Get single transaction by reference. # Get Transfer Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/get-transfer get /transfer Get transactions. # Initiate Transfer Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/initiate-transfer post /transfer/initiate Initiate transfer for KES or GHS. This is not required for NGN. # Withdraw Source: https://docs.getpartna.com/api-reference/endpoint/v3/transfer/withdraw post /transfer/withdraw Initiate withdrawal. # Create User Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/create-user post /user/activate Create and activate user accounts. # Delete API Key Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/delete-api-key delete /user/apikey Delete user API key. # Delete Webhook URL Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/delete-webhook-url delete /user/webhook Delete user webhook URL. # Generate API Key Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/generate-api-key post /user/apikey Generate user API keys. # Get API Key scopes Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-api-key-scopes get /user/apikey-scope Get the valid scopes for API jey generation. # Get API Keys Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-api-keys get /user/apikeys Get user API keys. # Get Balances Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-balances get /user/balances Get merchant and customer balances. # Get Customer's Account Profile Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-customers-account-profile get /user/profile Get customer's account profile. # Get Registration, Deposit and Fee Settings Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-registration-deposit-and-fee-settings get /user/settings Get preferred registration type, fee settings, deposit account, and crypto payment source. # Get Webhook URL Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/get-webhook-url get /user/webhook Get user webhook URL. # Invalidate API Key Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/invalidate-api-key post /user/invalidate-apikey Invalidate user API key. # Registration, Deposit and Fee Settings Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/registration-deposit-and-fee-settings put /user/settings Update preferred registration type, fee settings, deposit account, and crypto payment source. # Update User Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/update-user put /user Update user endpoint ID and callback URL. # Update Webhook URL Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/update-webhook-url post /user/webhook Update user webhook URL. # Used to delete an admin with privileges Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/used-to-delete-an-admin-with-privileges delete /user/admin Used to delete an admin with privileges. # Used to get all active admins with privileges Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/used-to-get-all-active-admins-with-privileges get /user/admin Used to get all active admins with privileges. # Used to give admin privileges. Source: https://docs.getpartna.com/api-reference/endpoint/v3/user/used-to-give-admin-privileges post /user/admin The first value of the roles array should be 'admin' followed by other permissions (view, operation, settings) # API Reference Source: https://docs.getpartna.com/v4/api-reference/overview Complete reference for all Partna API endpoints This is the complete reference for the Partna v4 API. All endpoints use the same base URL and authentication pattern. ## Base URLs | Environment | Base URL | | ----------- | -------------------------------------- | | Staging | `https://staging-api.getpartna.com/v4` | | Production | `https://api.getpartna.com/v4` | ## Authentication All requests require two headers: ```text theme={null} x-api-key: YOUR_API_KEY x-api-user: YOUR_USERNAME ``` See [Authentication](/v4/documentation/authentication) for details. ## Request format All `POST` and `PUT` requests accept JSON bodies. Set the `Content-Type: application/json` header. ## Response format All responses return JSON with this structure: ```json theme={null} { "data": { }, "message": "success" } ``` Error responses include a descriptive message: ```json theme={null} { "message": "Description of what went wrong" } ``` ## Endpoint groups **Account**: Create and manage user accounts, bank accounts, and profiles. **Ramp**: Initiate onramp (fiat-to-crypto) and offramp (crypto-to-fiat) transactions. This is the core of the Partna API. **Rate**: Fetch conversion rates and create/verify rate keys for locking prices. **Transfer**: Create transfers, initiate payouts, convert currencies, and withdraw balances. **KYC**: Identity verification flows including BVN verification, OTP confirmation, and bank account resolution. **Bank**: List supported banks by currency. **Discount**: Fetch current volume-based discount tiers. **Supported**: List supported crypto assets and networks. **User**: Manage merchant settings, API keys, webhook URLs, balances, and admin access. **MFA**: Enable and manage multi-factor authentication for accounts. **Mock (Staging Only)**: Simulate deposits for testing. # Authentication Source: https://docs.getpartna.com/v4/documentation/authentication Authenticate your API requests with API keys Every request to the Partna API must include two headers: `x-api-key` and `x-api-user`. ## Headers | Header | Description | | ------------ | ------------------------------------------------------------------------- | | `x-api-key` | Your API key. Provided during onboarding for both staging and production. | | `x-api-user` | Your merchant username. Provided during onboarding. | ## Example request ```bash cURL theme={null} curl --request GET \ --url https://api.getpartna.com/v4/account \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_MERCHANT_USERNAME' ``` ```python Python theme={null} import requests response = requests.get( "https://api.getpartna.com/v4/account", headers={ "x-api-key": "YOUR_API_KEY", "x-api-user": "YOUR_MERCHANT_USERNAME" } ) print(response.json()) ``` ```javascript Node.js theme={null} const response = await fetch("https://api.getpartna.com/v4/account", { headers: { "x-api-key": "YOUR_API_KEY", "x-api-user": "YOUR_MERCHANT_USERNAME" } }); const data = await response.json(); console.log(data); ``` ## Managing API keys You can generate, rotate, and revoke API keys programmatically: | Action | Endpoint | | ------------------ | ----------------------------------------------------------------------------------- | | Generate a new key | [POST /user/api-key](/api-reference/endpoint/v3/user/generate-api-key) | | List all keys | [GET /user/api-keys](/api-reference/endpoint/v3/user/get-api-keys) | | View key scopes | [GET /user/api-key/scopes](/api-reference/endpoint/v3/user/get-api-key-scopes) | | Invalidate a key | [POST /user/api-key/invalidate](/api-reference/endpoint/v3/user/invalidate-api-key) | | Delete a key | [DELETE /user/api-key](/api-reference/endpoint/v3/user/delete-api-key) | ## Security best practices **Never expose your API key in client-side code.** All API calls should be made from your backend server. The `x-api-key` grants full access to your merchant account. **Use separate keys for staging and production.** Your staging credentials only work against the staging environment and vice versa. **Rotate keys periodically.** Use the Generate API Key endpoint to create a new key, update your integration, then invalidate the old key. ## Getting credentials If you do not have credentials yet, [request access here](https://forms.getpartna.com/get-started). The Partna team will contact you to complete onboarding and set up your merchant account. Once your account is active, generate your API keys from the dashboard: * Staging: [staging-dashboard.getpartna.com](https://staging-dashboard.getpartna.com/settings?tab=api-webhooks) * Production: [dashboard.getpartna.com](https://dashboard.getpartna.com/settings?tab=api-webhooks) # Errors Source: https://docs.getpartna.com/v4/documentation/core-concepts/errors HTTP status codes and error response format The Partna API uses standard HTTP status codes. All error responses include a JSON body with a `message` field describing what went wrong. ## Error response format ```json theme={null} { "message": "A human-readable description of the error" } ``` Some error responses also include a `data` field with additional context. ## HTTP status codes | Code | Meaning | When you will see it | | ----- | --------------------- | -------------------------------------------------------------------------------------------------- | | `200` | OK | Request succeeded | | `201` | Created | Resource created successfully | | `400` | Bad Request | Missing required fields, invalid parameter values, or malformed request body | | `401` | Unauthorized | Missing or invalid `x-api-key` or `x-api-user` headers | | `403` | Forbidden | Your API key does not have permission for this action | | `404` | Not Found | The requested resource does not exist | | `409` | Conflict | A conflicting request is already in progress (e.g., a pending ramp request for the same user) | | `422` | Unprocessable Entity | The request is well-formed but cannot be processed (e.g., insufficient balance, KYC not completed) | | `429` | Too Many Requests | Rate limit exceeded. Back off and retry. | | `500` | Internal Server Error | Something went wrong on Partna's side. Contact support if this persists. | | `503` | Service Unavailable | The service is temporarily unavailable (e.g., rate service down). Retry after a short delay. | ## Common errors and solutions ### Authentication errors (401) **"Unauthorized"** or **"Invalid API key"**: Confirm that both `x-api-key` and `x-api-user` headers are present and correct. Staging and production credentials are not interchangeable. ### Ramp request errors (400, 409) **"User already has a pending ramp request"**: Each user can only have one active ramp request at a time. Either wait for it to complete or set `cancelPendingRampRequest: true` in your next ramp request to cancel the existing one and create a new one. **"Invalid rate key"**: The rate key has expired or is malformed. Fetch a fresh rate using [Get Rate](/api-reference/endpoint/v3/rate/get-rate) and use the new key. ### KYC errors (422) **"KYC not completed"**: The user account has not passed identity verification. Complete the KYC flow before initiating transactions. See [Accounts and KYC](/v4/documentation/guides/accounts-and-kyc). ### Balance errors (422) **"Insufficient balance"**: Your merchant payout balance does not have enough funds to cover the withdrawal. Fund your account before retrying. ## Retry guidance For `429` and `503` responses, use exponential backoff starting at 1 second. Do not retry `400`, `401`, `403`, or `422` errors without fixing the underlying issue first. `500` errors can be retried, but if they persist, contact [dev@getpartna.com](mailto:dev@getpartna.com). # Rates and Fees Source: https://docs.getpartna.com/v4/documentation/core-concepts/rates-and-fees Conversion rates, rate keys, and volume-based discounts Partna provides real-time conversion rates between supported currencies. Rates are fetched via the API and locked using rate keys before initiating a transaction. ## Fetching rates Use the [Get Rate](/api-reference/endpoint/v3/rate/get-rate) endpoint to fetch current conversion rates. ```bash theme={null} curl --request GET \ --url 'https://api.getpartna.com/v4/rate?fromCurrency=NGN&toCurrency=USDT&fromAmount=100000' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` The response includes a `key` field for each rate pair: ```json theme={null} { "data": { "rate": { "USDT_to_NGN": { "fromAmount": 1, "fromCurrency": "USDT", "key": "1640-USDT_to_NGN-a1b2c3d4-1755873777", "rate": 1640, "toAmount": 1640, "toCurrency": "NGN" } } }, "message": "success" } ``` ## Rate keys A rate key locks in a quoted conversion rate for a limited time. When you initiate an onramp or offramp request, pass the `rateKey` from the rate response to guarantee the quoted price. You can also create a custom rate key using the [Create Ratekey](/api-reference/endpoint/v3/rate/create-a-custom-ratekey-for-conversion) endpoint if you need to apply a custom spread or margin. Verify a rate key is still valid using [Verify Ratekey](/api-reference/endpoint/v3/rate/verify-rate-key). Rate keys expire. If a rate key expires before the transaction completes, the `expireAction` parameter on the ramp request controls what happens: | expireAction | Behavior | | ---------------- | ------------------------------------------------------------------------------------------------- | | `useCurrentRate` | The transaction proceeds at whatever the current rate is at settlement time. This is the default. | | `deposit` | The funds are deposited to the user's balance instead of completing the conversion. | ## Fee bearer When creating transactions, the `feeBearer` parameter controls who pays the transaction fee: | Value | Behavior | | ---------- | ---------------------------------------------------- | | `merchant` | Fees are deducted from your merchant balance | | `client` | Fees are deducted from the customer's payment amount | Configure the default fee bearer in your merchant settings via the [Update Settings](/api-reference/endpoint/v3/user/registration-deposit-and-fee-settings) endpoint. # Testing Source: https://docs.getpartna.com/v4/documentation/core-concepts/testing-1 Test your integration with mock transactions in staging You can use [the mock UI](https://staging-dashboard.getpartna.com/mock/deposit) on the staging dashboard to execute test transactions with a few clicks. The staging environment also includes mock API endpoints for more flexibility. Use them to test your integration end-to-end without moving real funds. ## Mock endpoints ### Mock Deposit Simulates a crypto deposit to a user's account. Use this to test webhook delivery for `Deposit` events and to fund test accounts for subsequent operations. ```bash theme={null} curl --request POST \ --url https://staging-api.getpartna.com/v4/mock/deposit \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "amount": 100, "currency": "USDT" }' ``` See the full reference: [Mock Deposit](/api-reference/endpoint/v3/mock/mock-deposit) ### Mock Fiat Deposit Simulates a fiat (local currency) deposit. Use this to test the collect flow and onramp scenarios where a user pays in fiat. ```bash theme={null} curl --request POST \ --url https://staging-api.getpartna.com/v4/mock/fiat-deposit \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "amount": 50000, "currency": "NGN" }' ``` See the full reference: [Mock Fiat Deposit](/api-reference/mock/mock-fiat-deposit) You can also run these from the staging dashboard UI: [Mock Deposits](https://staging-dashboard.getpartna.com/mock/deposit). ## Testing workflow A typical testing workflow looks like this: Use [Create Account](/api-reference/account/create-account) to set up a test user. In staging, KYC can be completed with test data. Run through the [KYC flow](/guides/accounts-and-kyc) using test identity data. In staging, you can use any 11-digit BVN, `08030013843` for phone verification, and `123456` as the OTP. Call [Get Rate](/api-reference/rate/get-rate) to get a conversion rate and rate key. Use [Onramp and Offramp](/api-reference/ramp/onramp-and-offramp) to create a test transaction. For onramp: call Mock Fiat Deposit to simulate the user's local currency payment. For offramp: call Mock Deposit to simulate an incoming crypto payment. Confirm your webhook endpoint received the expected events (`Deposit`, `Convert`, `Transfer`, etc.) with valid signatures. ## Webhook testing tips If you do not have a publicly accessible server during development, use a tunneling tool like [ngrok](https://ngrok.com) or [webhook.site](https://webhook.site) to expose your local endpoint: ```bash theme={null} ngrok http 3000 ``` Then set your webhook URL to the forwarding address (e.g., `https://abc123.ngrok.io/webhook`) via the [Update Webhook URL](/api-reference/user/update-webhook-url) endpoint. If you just want to inspect payloads without writing code, webhook.site gives you a unique URL that logs every request it receives. ## Going to production When your staging integration is working correctly: 1. Swap the base URL from `https://staging-api.getpartna.com/v4` to `https://api.getpartna.com/v4` 2. Replace staging credentials with your production `x-api-key` and `x-api-user` 3. Update your webhook signature verification to use the production public key 4. Update your webhook URL to your production endpoint No other code changes are required. The API behaves identically in both environments. # Webhooks Source: https://docs.getpartna.com/v4/documentation/core-concepts/webhooks Receive real-time notifications for transaction and verification events Webhooks notify your application in real time when transaction events occur. Configure a webhook URL to receive updates for deposits, conversions, transfers, onramp requests, and offramp requests. ## Setup Configure your webhook URL through the [dashboard](/v4/documentation/environments#dashboard) or programmatically via the [Update Webhook URL](/api-reference/endpoint/v3/user/update-webhook-url) endpoint. Your endpoint must be a publicly accessible `POST` URL that returns a `200` status code immediately. Process events asynchronously after acknowledging receipt. ## Receiving events Every webhook payload includes three fields: | Field | Type | Description | | ----------- | ------ | ------------------------------------------------------- | | `event` | string | The event type (e.g., `Deposit`, `Convert`, `Transfer`) | | `data` | object | Event-specific payload | | `signature` | string | RSA signature for verification | ## Supported events | Event | Description | | ---------------------- | -------------------------------------------------- | | `Deposit` | A fiat or crypto deposit was received | | `Convert` | A currency conversion completed | | `Transfer` | A transfer (payout/withdrawal) status changed | | `Onramp` | An onramp (fiat-to-crypto) request status changed | | `Withdrawal` | A withdrawal was processed | | `Offramp` | An offramp (crypto-to-fiat) request status changed | | `verification.success` | A user successfully completed KYC verification | | `verification.failed` | A user failed KYC verification (includes reason) | ### Sample events ```json Deposit theme={null} { "event": "Deposit", "data": { "amount": 5001, "toUser": "chng", "currency": "NGN", "toAccount": "chng", "status": "completed", "sessionId": "3c58db9", "transactionMemo": "Mock Deposit" }, "signature": "m9STu6pcsviYMfgu5JBjuR" } ``` ```json Convert theme={null} { "event": "convert", "data": { "transactionReference": "a1fe4aa5679d7", "fromUser": "chng", "toAccount": "chng", "fromAmount": 5, "toAmount": 6000.05, "fromCurrency": "USD", "toCurrency": "NGN" }, "signature": "gTZdvoadZscP3g==" } ``` ```json Transfer theme={null} { "event": "Transfer", "data": { "transactionReference": "a16db1274783", "amount": 1, "fromUser": "chngr", "toUser": "chngr", "currency": "USD", "fromAccount": "chngr", "toAccount": "rhmn", "status": "completed" }, "signature": "C73lZKC80Cg==" } ``` ```json Onramp theme={null} { "event": "Onramp", "data": { "transactionReference": "a1e681b09cc7ccea", "toUser": "chngr", "toAccount": "chngr", "status": "completed", "sessionId": "52567c9fb6864a8c94549675c70cc9a8", "fromAmount": 149775, "toAmount": 109.99175768162324, "fromCurrency": "NGN", "toCurrency": "USDT", "transactionMemo": "Mock Deposit" }, "signature": "Wh+Jqnxc2Iw==" } ``` ```json Withdrawal theme={null} { "event": "Withdrawal", "data": { "transactionReference": "a1e687ccea", "amount": 109.9917, "fromUser": "chang", "currency": "USDT", "network": "tron", "fromAccount": "chang", "toAddress": "TSqRgHDE1Nypqo1LUp6Q", "status": "processing", "transactionHash": "3fa07c748260f469896bf" }, "signature": "dC8YIklLc08uiGxA==" } ``` ```json Offramp theme={null} { "event": "Offramp", "data": { "transactionReference": "691af31ba66789", "toUser": "chng", "toAccount": "chng", "status": "completed", "transactionHash": "99d7c7b5f8f3447d0cc20279f8bab2f827558c5b76", "fromAmount": 84.2, "toAmount": 99879.3927, "fromCurrency": "USDT", "toCurrency": "NGN" }, "signature": "faUgOe8SYUfBlvRWsDxg==" } ``` ## Verifying signatures Every webhook includes an RSA signature. Always verify the signature before processing an event to confirm it came from Partna. The signature is generated by signing the JSON-encoded `data` field with Partna's RSA private key using SHA-256 with PSS padding. Verify it against the corresponding public key for your environment. ```javascript Node.js theme={null} import crypto from "crypto"; const SAMPLE_WEBHOOK_REQUEST = `{"event":"Convert","data":{"transactionReference":"a1632e4ea53f2802846cf7797d29bfba","fromUser":"eltnegbiz2","toAccount":"eltnegbiz2","timestamp":1775132847,"fromAmount":1,"toAmount":1000.5,"fromCurrency":"USD","toCurrency":"NGN"},"signature":"ISPOtoKOJRSuT0rKoz0P7JSPSna6UztGOrkKJKWSn62SqUobtaCYURVbhE6A8eIt82qJ9+32XlK1GJZkA5A64MYuDBLF9MdlHvfuNDkPRTof4RV+8UdlSifmBqAU2oYtvA6z4aFHUdiRcfpPNSdkRmLARwIbBxv9Zg6GHFV5HKqybMNt0QaCQskUWVcWOnCEf9PpKV1cKhJWWchYNpiCJgcV8N/vUgOFFVymhkXzbfZADuLukEwScJ751Ll97wWpLwZ1s+1DoBNQ5cY32ft80c3fwnO1NpAW/n22Bpf82rdLP1JctaJUV/3yuorMh+qNaKaXocAMpn6Vkxu8NdyNdQ=="}`; const PUBLIC_KEY = `-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAv2ipgHLFFHgGHr9VpsPN8V1HIbCrlmTZRU/CYSDaoVX+xerJOMGX qmwgQMQH5T81VaMw4rtIA8tT4DkJgjb+7G0x4CGK1OPdlvhEGP2mOFy02onkEnMv uN3glVc4YKLvWDTG0KT7q9mARBIkO2Nrwy6IVHAl9pMXMJTRS22c0cIbuRmkYsGZ trylUv50knbRSgy5EA6523+j3PPJB4TgsigGSJxJGuksaxnDQGRE558xnyw/0gJm mAIdbxboQTGMqod/My/kAssRkUNu1QtqrsdhZmGYHS+pIPJSaxqHEy8eiTahoqqq 8KgNUfQfwduG+Kc4f/t5JHetSt1dgulmswIDAQAB -----END RSA PUBLIC KEY-----`; function verifySignature(data: string, signature: string) { return crypto.verify( "sha256", Buffer.from(JSON.stringify(data), "utf8"), { key: PUBLIC_KEY, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, }, Buffer.from(signature, "base64"), ); } function run() { const { event, data, signature } = JSON.parse(SAMPLE_WEBHOOK_REQUEST); const isValid = verifySignature(data, signature); if (isValid) { console.log("Signature is valid. Processing event:", event); // Process the event data as needed } else { console.error("Invalid signature. Possible tampering detected."); } } run(); ``` ```python Python theme={null} # install dependencies with `pip install cryptography` import base64 import json from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding SAMPLE_WEBHOOK_REQUEST = b'{"event":"Convert","data":{"transactionReference":"a1632e4ea53f2802846cf7797d29bfba","fromUser":"eltnegbiz2","toAccount":"eltnegbiz2","timestamp":1775132847,"fromAmount":1,"toAmount":1000.5,"fromCurrency":"USD","toCurrency":"NGN"},"signature":"ISPOtoKOJRSuT0rKoz0P7JSPSna6UztGOrkKJKWSn62SqUobtaCYURVbhE6A8eIt82qJ9+32XlK1GJZkA5A64MYuDBLF9MdlHvfuNDkPRTof4RV+8UdlSifmBqAU2oYtvA6z4aFHUdiRcfpPNSdkRmLARwIbBxv9Zg6GHFV5HKqybMNt0QaCQskUWVcWOnCEf9PpKV1cKhJWWchYNpiCJgcV8N/vUgOFFVymhkXzbfZADuLukEwScJ751Ll97wWpLwZ1s+1DoBNQ5cY32ft80c3fwnO1NpAW/n22Bpf82rdLP1JctaJUV/3yuorMh+qNaKaXocAMpn6Vkxu8NdyNdQ=="}' PUBLIC_KEY_PEM = b"""-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAv2ipgHLFFHgGHr9VpsPN8V1HIbCrlmTZRU/CYSDaoVX+xerJOMGX qmwgQMQH5T81VaMw4rtIA8tT4DkJgjb+7G0x4CGK1OPdlvhEGP2mOFy02onkEnMv uN3glVc4YKLvWDTG0KT7q9mARBIkO2Nrwy6IVHAl9pMXMJTRS22c0cIbuRmkYsGZ trylUv50knbRSgy5EA6523+j3PPJB4TgsigGSJxJGuksaxnDQGRE558xnyw/0gJm mAIdbxboQTGMqod/My/kAssRkUNu1QtqrsdhZmGYHS+pIPJSaxqHEy8eiTahoqqq 8KgNUfQfwduG+Kc4f/t5JHetSt1dgulmswIDAQAB -----END RSA PUBLIC KEY-----""" def verify_signature(payload: bytes, signature: str) -> bool: public_key = serialization.load_pem_public_key(PUBLIC_KEY_PEM) sig_bytes = base64.b64decode(signature) try: public_key.verify( sig_bytes, payload, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256(), ) return True except Exception: return False def main(): json_data = json.loads(SAMPLE_WEBHOOK_REQUEST) event = json_data["event"] signature = json_data["signature"] # extract data bytes payload = SAMPLE_WEBHOOK_REQUEST[ SAMPLE_WEBHOOK_REQUEST.index(b'"data":') + 7 : SAMPLE_WEBHOOK_REQUEST.index(b"}") + 1 ] if verify_signature(payload, signature): print(f"Signature valid for event: {event}") # Process the event data as needed else: print("Invalid signature") if __name__ == "__main__": main() ``` ```go Go theme={null} package main import ( "crypto" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" ) var ( SAMPLE_WEBHOOK_REQUEST = `{"event":"Convert","data":{"transactionReference":"a1632e4ea53f2802846cf7797d29bfba","fromUser":"eltnegbiz2","toAccount":"eltnegbiz2","timestamp":1775132847,"fromAmount":1,"toAmount":1000.5,"fromCurrency":"USD","toCurrency":"NGN"},"signature":"ISPOtoKOJRSuT0rKoz0P7JSPSna6UztGOrkKJKWSn62SqUobtaCYURVbhE6A8eIt82qJ9+32XlK1GJZkA5A64MYuDBLF9MdlHvfuNDkPRTof4RV+8UdlSifmBqAU2oYtvA6z4aFHUdiRcfpPNSdkRmLARwIbBxv9Zg6GHFV5HKqybMNt0QaCQskUWVcWOnCEf9PpKV1cKhJWWchYNpiCJgcV8N/vUgOFFVymhkXzbfZADuLukEwScJ751Ll97wWpLwZ1s+1DoBNQ5cY32ft80c3fwnO1NpAW/n22Bpf82rdLP1JctaJUV/3yuorMh+qNaKaXocAMpn6Vkxu8NdyNdQ=="}` PUBLIC_KEY_PEM = `-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAv2ipgHLFFHgGHr9VpsPN8V1HIbCrlmTZRU/CYSDaoVX+xerJOMGX qmwgQMQH5T81VaMw4rtIA8tT4DkJgjb+7G0x4CGK1OPdlvhEGP2mOFy02onkEnMv uN3glVc4YKLvWDTG0KT7q9mARBIkO2Nrwy6IVHAl9pMXMJTRS22c0cIbuRmkYsGZ trylUv50knbRSgy5EA6523+j3PPJB4TgsigGSJxJGuksaxnDQGRE558xnyw/0gJm mAIdbxboQTGMqod/My/kAssRkUNu1QtqrsdhZmGYHS+pIPJSaxqHEy8eiTahoqqq 8KgNUfQfwduG+Kc4f/t5JHetSt1dgulmswIDAQAB -----END RSA PUBLIC KEY-----` ) func VerifyData(data []byte, signature string) bool { sig, err := base64.StdEncoding.DecodeString(signature) if err != nil { fmt.Println(err) return false } block, _ := pem.Decode([]byte(PUBLIC_KEY_PEM)) if block == nil { fmt.Println("failed to parse PEM block containing the public key") return false } pubkey, _ := x509.ParsePKCS1PublicKey(block.Bytes) msgHash := sha256.New() msgHash.Write(data) msgHashSum := msgHash.Sum(nil) err = rsa.VerifyPSS(pubkey, crypto.SHA256, msgHashSum, sig, nil) return err == nil } func main() { var data struct { Event string `json:"event"` Data json.RawMessage `json:"data"` Signature string `json:"signature"` } json.Unmarshal([]byte(SAMPLE_WEBHOOK_REQUEST), &data) isValid := VerifyData(data.Data, data.Signature) if isValid { fmt.Println("Signature is valid") } else { fmt.Println("Signature is invalid") } } ``` ## Public keys Use the correct public key for your environment. ### Staging ```text theme={null} -----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAv2ipgHLFFHgGHr9VpsPN8V1HIbCrlmTZRU/CYSDaoVX+xerJOMGX qmwgQMQH5T81VaMw4rtIA8tT4DkJgjb+7G0x4CGK1OPdlvhEGP2mOFy02onkEnMv uN3glVc4YKLvWDTG0KT7q9mARBIkO2Nrwy6IVHAl9pMXMJTRS22c0cIbuRmkYsGZ trylUv50knbRSgy5EA6523+j3PPJB4TgsigGSJxJGuksaxnDQGRE558xnyw/0gJm mAIdbxboQTGMqod/My/kAssRkUNu1QtqrsdhZmGYHS+pIPJSaxqHEy8eiTahoqqq 8KgNUfQfwduG+Kc4f/t5JHetSt1dgulmswIDAQAB -----END RSA PUBLIC KEY----- ``` ### Production ```text theme={null} -----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAoGmOqVUxIGq92P8J5KqwJbwucsNeUwfS76saj0UPgT1IsK/mvceR Ges7dsgE4EVx7Wsd5PPNeAfaNt8d0plBLhHRW64WFyv6jYcYp8eVdHUxWLA6p5gZ 9rGrZiwqKqunppTPlV04gdDC32rAbpAR3IMYmMJLuPy63Oumszl4qk69A1o60Son r0KYBaRK7aQsFT9IFexicDUhrF1SohaNH/msTdvJb0SwSGiV92EhmmC2R8CL83/B S8QC5c9PWtZ4a26CRLHe0IfaGGz8ClhlO8IFxz0cNrpoRa4JRsffezQ+RMQqCxhc igC7wHfqZr5BtziQOYJjUknVzsd81HEYlwIDAQAB -----END RSA PUBLIC KEY----- ``` ## Best practices **Always verify signatures.** Never process a webhook event without verifying the signature first. This prevents spoofed events from affecting your system. **Return 200 immediately.** Acknowledge receipt before processing. If your endpoint takes too long to respond, the delivery may be retried. **Handle duplicates.** Use the `sessionId` or `rampReference` in the event data as an idempotency key. The same event may be delivered more than once. **Log raw payloads.** Store the raw webhook body for debugging. If something goes wrong, the raw payload is essential for troubleshooting. # Environments Source: https://docs.getpartna.com/v4/documentation/environments Staging and production base URLs Partna provides separate staging and production environments. Build and test in staging before deploying to production. ## Base URLs | Environment | Base URL | | -------------- | -------------------------------------- | | **Staging** | `https://staging-api.getpartna.com/v4` | | **Production** | `https://api.getpartna.com/v4` | All endpoint paths in this documentation are relative to these base URLs. For example, the Get Rate endpoint is: * Staging: `https://staging-api.getpartna.com/v4/rate` * Production: `https://api.getpartna.com/v4/rate` ## Widget URLs Use these base URLs for widget integrations: | Environment | Widget Base URL | | -------------- | ------------------------------------ | | **Staging** | `https://staging-pay.getpartna.com/` | | **Production** | `https://pay.getpartna.com/` | ## Staging environment The staging environment mirrors production behavior but uses test data. Key differences: * **No real money moves.** Transactions are simulated. * **Mock endpoints available.** Use [Mock Deposit](/api-reference/endpoint/v3/mock/mock-deposit) and [Mock Fiat Deposit](/api-reference/endpoint/v3/mock/mock-fiat-deposit) to simulate incoming payments and test your webhook handling. * **Separate credentials.** Your staging `x-api-key` and `x-api-user` are different from production. They are provided during onboarding. Use staging to validate your integration end-to-end before switching to production credentials. ## Production environment The production environment processes real transactions with real funds. Switch to production by changing the base URL and credentials. No other code changes are required. ## Dashboard Manage your merchant settings, view transactions, and configure webhooks: | Environment | Dashboard | | :------------- | :-------------------------------------------------------------------------- | | **Staging** | [staging-dashboard.getpartna.com](https://staging-dashboard.getpartna.com/) | | **Production** | [dashboard.getpartna.com](https://dashboard.getpartna.com/) | # Accounts and KYC Source: https://docs.getpartna.com/v4/documentation/guides/accounts-and-kyc Create user accounts and verify identity Every end user who transacts through Partna needs an account and must complete identity verification (KYC). This is a regulatory requirement for all transactions. Users only need to verify once. ## Creating an account Use [Create Account](/api-reference/endpoint/v3/account/create-account) to register a new user: ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/account \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "email": "user@example.com", "accountName": "Jane Doe" }' ``` You can also use [Register Account](/api-reference/endpoint/v3/account/register-account) if your merchant registration type is set to "open", which creates a pending registration that the user can complete later. ## KYC verification flow Identity verification in Nigeria uses BVN (Bank Verification Number). The flow depends on the user's country and the available verification methods. Call [Initiate KYC](/api-reference/endpoint/v3/kyc/initiate-bvn-kyc) with the user's identity information (e.g., BVN for Nigeria). This triggers an OTP to be sent to the phone number associated with the user's identity. In staging, you can use any 11-digit number as the BVN to simulate a successful verification. ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/kyc/initiate \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "bvn": "12345678901", "accountName": "user-account-name" }' ``` After OTP verification, confirm the user's phone number using [Confirm Phone](/api-reference/endpoint/v3/kyc/confirm-phone). In staging, use `08030013843` when asked to verify the user's phone number. Call [Supported Verification Methods](/api-reference/endpoint/v3/kyc/supported-verification-methods) to see which identity verification methods are available for a given country. ```bash theme={null} curl --request GET \ --url 'https://api.getpartna.com/v4/kyc/verification-methods?country=NG' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` Once the user receives the OTP, submit it using [Confirm KYC OTP](/api-reference/endpoint/v3/kyc/confirm-kyc-otp). In staging, use `123456` as the OTP to simulate a successful verification. ```bash theme={null} curl --request PUT \ --url https://api.getpartna.com/v4/kyc/confirm-otp \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "otp": "123456", "accountName": "user-account-name" }' ``` In some cases, the user may need to select how they receive the OTP (e.g., SMS or phone call). After successful verification, you will receive a `verification.success` webhook event or a `verification.failed` event with reason if verification failed. If successful, the user can now transact. ## Merchant-managed KYC If you have already verified your users' identities through your own KYC process, you can use [Merchant KYC Manually](/api-reference/endpoint/v3/kyc/merchant-kyc-users-manually) to mark a user as verified without going through Partna's KYC flow. This requires approval from the Partna team during onboarding. ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/kyc/merchant-kyc \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "accountName": "user-account-name", "firstName": "Jane", "lastName": "Doe", "dateOfBirth": "1990-01-15" }' ``` ## Resolving bank accounts Before initiating payouts or offramp transactions, verify the destination bank account using [Resolve Bank](/api-reference/endpoint/v3/kyc/resolve-bank): ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/kyc/resolve-account \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "accountNumber": "0123456789", "bankCode": "044", "currency": "NGN" }' ``` This returns the account holder's name so you can confirm it matches before sending funds. ## Creating bank accounts for users Use [Create Bank Account](/api-reference/endpoint/v3/account/create-bank-account) to link a verified bank account to a user's Partna account. This is required before the user can receive local currency payouts. ## Account management | Action | Endpoint | | ------------------- | ------------------------------------------------------------------------------------- | | Get account details | [Get Account](/api-reference/endpoint/v3/account/get-account) | | List accounts | [Get Accounts Details](/api-reference/endpoint/v3/account/get-accounts-details) | | Get user profile | [Get Account Profile](/api-reference/endpoint/v3/account/get-account-profile) | | Update email | [Update Account Email](/api-reference/endpoint/v3/account/update-account-email) | | Check permissions | [Get Account Permissions](/api-reference/endpoint/v3/account/get-account-permissions) | | Search accounts | [Search Account](/api-reference/endpoint/v3/account/search-account) | # Collect and Settle Source: https://docs.getpartna.com/v4/documentation/guides/collect-and-settle Collect local currency payments and settle in USD or stablecoins The collect and settle flow lets you accept payments from African customers in local currency and receive settlement in USD or stablecoins. This is the solution for companies that sell services to African customers and need to get their money out efficiently. ## How it works After receiving your credentials, configure your merchant account: * **Callback URL**: Your webhook endpoint for payment notifications * **Fee bearer**: Whether you or your customer absorbs transaction fees * **Credit currency**: The currency you want to be settled in (e.g., `USD`, `USDT`, `USDC`) Configure these via the [dashboard](https://dashboard.getpartna.com) or the [Update Settings](/api-reference/endpoint/v3/user/registration-deposit-and-fee-settings) endpoint. Register each customer using [Create Account](/api-reference/endpoint/v3/account/create-account). This creates a permanent payment account that your customer can deposit to at any time. Your customer must verify their identity before making payments. See [Accounts and KYC](/v4/documentation/guides/accounts-and-kyc) for the verification flow. This is a one-time step. Use the onramp flow to collect a payment. Call [Onramp and Offramp](/api-reference/endpoint/v3/ramp/onramp-and-offramp) with `type: "fiatToCrypto"` to create a payment request. The response includes the bank account details where your customer should send their payment. Alternatively, if you are using the collect API for simpler collection without crypto conversion, you can create a payment using [Create Transfer](/api-reference/endpoint/v3/transfer/create-transfer) or [Initiate Transfer](/api-reference/endpoint/v3/transfer/initiate-transfer). Your customer transfers the specified local currency amount to the provided bank account via bank transfer. Partna converts the local currency and settles in your configured credit currency. You receive a webhook notification when the settlement completes. ## Settlement currencies You can settle in any supported stablecoin (USDT, USDC) or hold balances in local currency. Configure your preferred settlement currency in your merchant settings. ## Checking your balance Use [Get Balances](/api-reference/endpoint/v3/user/get-balances) to check your merchant account balances across all currencies: ```bash theme={null} curl --request GET \ --url https://api.getpartna.com/v4/user/balances \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` ## Withdrawing your balance Once funds are settled in your account, use [Withdraw](/api-reference/endpoint/v3/transfer/withdraw) to send funds to an external wallet or bank account. ## Currency conversion If you hold balances in one currency and need to convert to another, use [Convert Currency](/api-reference/endpoint/v3/transfer/convert-currency) to convert between supported currencies within your account. ## Use case: SaaS company collecting from Nigerian clients A typical flow for a SaaS company invoicing Nigerian enterprises: 1. Customer is onboarded and KYC'd once 2. For each invoice, create a ramp request specifying the NGN amount owed 3. Share the generated bank account details with your customer 4. Customer pays via standard Nigerian bank transfer 5. You receive USDT or USD settlement within hours (typically same-day) 6. Withdraw to your corporate wallet or bank account as needed This replaces the traditional process of waiting weeks or months for cross-border wire transfers from Nigeria. # Offramp Source: https://docs.getpartna.com/v4/documentation/guides/offramp Let your users convert crypto to local currency The offramp flow lets your users send crypto (USDT, USDC, BTC, ETH) and receive local currency (NGN, GHS, KES) in their bank account or mobile money wallet. Partna handles the crypto receipt, conversion, and local currency payout. If you want full control over the experience, use this API flow. If you would rather redirect users to a Partna payout flow, use the [Offramp Widget](/v4/documentation/guides/offramp-widget) instead. ## How it works Register your user with Partna using [Create Account](/api-reference/endpoint/v3/account/create-account) if they do not already have one. Verify the user's bank account details using [Resolve Bank](/api-reference/endpoint/v3/kyc/resolve-bank). This confirms the account number and returns the account holder's name. ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/kyc/resolve-account \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "accountNumber": "0123456789", "bankCode": "044", "currency": "NGN" }' ``` Response: ```json theme={null} { "data": { "accountName": "JANE DOE", "accountNumber": "0123456789" }, "message": "success" } ``` Call [Get Rate](/api-reference/endpoint/v3/rate/get-rate) with `fromCurrency=USDT` and `toCurrency=NGN`. Save the rate `key`. ```bash theme={null} curl --request GET \ --url 'https://api.getpartna.com/v4/rate?fromCurrency=USDT&toCurrency=NGN&fromAmount=100' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` Call [Onramp and Offramp](/api-reference/endpoint/v3/ramp/onramp-and-offramp) with `type: "cryptoToFiat"`. Include the rate key, the user's bank details, and the crypto amount. ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/ramp \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "type": "cryptoToFiat", "fromCurrency": "USDT", "fromNetwork": "tron", "fromAmount": 100, "toCurrency": "NGN", "toNetwork": "naira", "accountName": "JANE DOE", "accountNumber": "0123456789", "bankCode": "044", "rateKey": "RATE_KEY_FROM_STEP_4", "rampReference": "your-unique-reference-id" }' ``` The response includes a crypto wallet address where the user should send their USDT. The user sends the specified crypto amount to the wallet address returned in step 5. Once Partna confirms the crypto deposit, the equivalent local currency is sent to the user's bank account. A webhook event is dispatched to your endpoint. ## Key parameters | Parameter | Description | | --------------- | --------------------------------------------------------------------------- | | `type` | Must be `cryptoToFiat` for offramp | | `fromCurrency` | The crypto being sent (e.g., `USDT`, `USDC`, `BTC`) | | `fromNetwork` | The blockchain network (e.g., `tron`, `ethereum`, `solana`) | | `toCurrency` | The local currency for payout (e.g., `NGN`) | | `toNetwork` | The payout network (e.g., `naira`, `mobileMoney`, `mpesa`) | | `accountName` | The bank account holder's name (from Resolve Bank) | | `accountNumber` | The destination bank account number | | `bankCode` | The bank code (from [Get Banks](/api-reference/endpoint/v3/bank/get-banks)) | | `rateKey` | The rate key from [Get Rate](/api-reference/endpoint/v3/rate/get-rate) | | `rampReference` | Your unique reference for this transaction | ## Custom payout amounts By default, Partna pays the full converted amount to the user's bank account. If you want to pay a different amount (e.g., to build in a margin), you can adjust the payout using the [Withdraw](/api-reference/endpoint/v3/transfer/withdraw) endpoint after the crypto deposit is confirmed. This requires sufficient funds in your payout balance. ## Getting bank codes Use [Get Banks](/api-reference/endpoint/v3/bank/get-banks) to fetch the list of supported banks and their codes for a given currency: ```bash theme={null} curl --request GET \ --url 'https://api.getpartna.com/v4/bank?currency=NGN' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` # Offramp Widget Source: https://docs.getpartna.com/v4/documentation/guides/offramp-widget Redirect users to Partna's offramp widget The offramp widget lets you redirect users to a Partna-managed flow where they can send crypto and receive local currency. Use this flow when you want Partna to handle the payout collection interface and transaction instructions. ## Base URLs | Environment | URL | | ------------ | -------------------------------------------------- | | `staging` | `https://staging-pay.getpartna.com/v4/pay/offramp` | | `production` | `https://pay.getpartna.com/v4/pay/offramp` | ## Query parameters | Parameter | Required | Description | | --------------- | -------- | ----------------------------------------------------------------------- | | `amount` | Yes | Amount the user wants to convert | | `from_currency` | Yes | Crypto the user is sending, such as `USDT`, `USDC`, `BTC`, or `ETH` | | `from_network` | Yes | Network the user is sending from, such as `tron`, `celo`, or `ethereum` | | `to_currency` | Yes | Local payout currency, such as `NGN`, `KES`, or `GHS` | | `merchant` | Yes | Your Partna merchant username | | `address` | No | Wallet address the crypto will be sent from | | `reference` | No | A hex string transaction reference | ## Registration modes The widget supports two merchant-controlled registration modes: | Mode | Behavior | | -------- | ---------------------------------------------------------------------------------- | | `open` | Users can begin registration through the widget flow and complete onboarding later | | `closed` | Only users you have already created can continue in the widget flow | You can manage this setting through [Update Settings](/api-reference/endpoint/v3/user/registration-deposit-and-fee-settings) and inspect the current value with [Get Settings](/api-reference/endpoint/v3/user/get-registration-deposit-and-fee-settings). If your registration mode is `open`, you can use [Register Account](/api-reference/endpoint/v3/account/register-account) to create a pending registration. If your registration mode is `closed`, create the user up front with [Create Account](/api-reference/endpoint/v3/account/create-account). The hosted flow collects the user's payout details before finalizing the transaction. ## How it works Build a URL using the correct environment URL and the required query parameters. Send the user to the hosted Partna offramp flow from your app or website. The user enters the required payout details in the hosted flow and transfers the specified crypto amount. Partna sends `Offramp` webhook events as the transaction progresses. See [Webhooks](/v4/documentation/core-concepts/webhooks) for verification details. Once the transaction reaches `completed`, the local currency payout has been sent. ## Build the redirect URL ```typescript TypeScript theme={null} const redirectURL = environment === "staging" ? "https://staging-pay.getpartna.com/v4/pay/offramp" : "https://pay.getpartna.com/v4/pay/offramp"; const params = new URLSearchParams(); params.append("from_currency", fromCurrency); params.append("to_currency", toCurrency); params.append("from_network", fromNetwork); params.append("amount", amount); if (address) { params.append("address", address); } params.append("merchant", merchant); if (reference) { params.append("reference", reference); } const url = `${redirectURL}?${params.toString()}`; window.location.href = url; ``` ```javascript JavaScript theme={null} const redirectURL = environment === "staging" ? "https://staging-pay.getpartna.com/v4/pay/offramp" : "https://pay.getpartna.com/v4/pay/offramp"; const params = new URLSearchParams({ from_currency: fromCurrency, to_currency: toCurrency, from_network: fromNetwork, amount, merchant, }); if (address) { params.append("address", address); } if (reference) { params.append("reference", reference); } window.location.href = `${redirectURL}?${params.toString()}`; ``` ## Example redirect URL ```text theme={null} https://pay.getpartna.com/v4/pay/offramp?amount=0.11&from_currency=CUSD&from_network=celo&to_currency=KES&address=0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d&merchant=your-merchant-username&reference=a13953b727b9f20ab1ab76dfd9abe4ca ``` For staging, use `https://staging-pay.getpartna.com/v4/pay/offramp` instead. ## Transaction lifecycle Partna emits `Offramp` webhooks with these statuses: | Status | Meaning | | ------------ | ------------------------------------------------------------------- | | `pending` | The offramp request has been created and is awaiting crypto payment | | `received` | Partna has received the user's crypto transfer | | `processing` | Conversion or payout is currently being processed | | `completed` | The local currency payout is complete | ## Sample webhook payloads ### `pending` ```json theme={null} { "event": "Offramp", "data": { "transactionReference": "a13953b727b9f20ab1ab76dfd9abe4ca", "status": "pending", "timestamp": 1773706850, "fromAmount": 0.11, "toAmount": 12.93, "fromCurrency": "CUSD", "toCurrency": "KES", "fromNetwork": "celo", "toNetwork": "kenyanshilling", "totalFeesInFromCurrency": 0.0089, "totalFeesInToCurrency": 1.14, "sendingAddress": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d", "paymentAddress": "0xc89f5afe4d5c13daeef96164abc8001c1e320718", "expireAt": 1773710450, "toExternalAccount": "Safaricom::0726140198::::MOBILE" }, "signature": "BASE64_SIGNATURE" } ``` ### `received` ```json theme={null} { "event": "Offramp", "data": { "transactionReference": "a13953b727b9f20ab1ab76dfd9abe4ca", "status": "received", "timestamp": 1773706902, "transactionHash": "0xeefee6c33e572458e6aef553b1572c58084ad59d7f3bcec9654d60810def6d2b", "fromAmount": 0.11, "fromCurrency": "CUSD", "toCurrency": "KES", "sendingAddress": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d" }, "signature": "BASE64_SIGNATURE" } ``` ### `processing` ```json theme={null} { "event": "Offramp", "data": { "transactionReference": "a13953b727b9f20ab1ab76dfd9abe4ca", "status": "processing", "timestamp": 1773706921, "fromAmount": 0.11, "toAmount": 12.9392, "fromCurrency": "CUSD", "toCurrency": "KES", "fromNetwork": "celo", "toNetwork": "kenyanshilling", "sendingAddress": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d", "expireAt": 1773710450, "toExternalAccount": "Safaricom::0726140198::::MOBILE" }, "signature": "BASE64_SIGNATURE" } ``` ### `completed` ```json theme={null} { "event": "Offramp", "data": { "transactionReference": "a13953b727b9f20ab1ab76dfd9abe4ca", "status": "completed", "timestamp": 1773706921, "sessionId": "DFE48F597F", "fromAmount": 0.11, "toAmount": 12.9392, "fromCurrency": "CUSD", "toCurrency": "KES", "fromNetwork": "celo", "toNetwork": "kenyanshilling", "sendingAddress": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d", "toExternalAccount": "Safaricom::0726140198::::MOBILE" }, "signature": "BASE64_SIGNATURE" } ``` ## When to use the hosted widget Use the hosted widget when you want: * A redirect-based offramp experience with less frontend work * Partna to collect payout details inside the hosted flow * A fast way to launch crypto-to-fiat payouts Use the API-based [Offramp Guide](/v4/documentation/guides/offramp) when you need to manage account resolution, payout details, and user experience yourself. # Onramp Source: https://docs.getpartna.com/v4/documentation/guides/onramp Let your users convert local currency to crypto The onramp flow lets your users pay in local currency (NGN, GHS, KES) and receive crypto (USDT, USDC, BTC, ETH) in their wallet. Partna handles the payment collection, currency conversion, and crypto delivery. If you want full control over the experience, use this API flow. If you would rather redirect users to a Partna checkout, use the [Onramp Widget](/v4/documentation/guides/onramp-widget) instead. ## How it works Register your user with Partna using [Create Account](/api-reference/endpoint/v3/account/create-account). This creates a permanent account that can receive payments at any time. Your user must verify their identity before transacting. See [Accounts and KYC](/v4/documentation/guides/accounts-and-kyc) for the full verification flow. Users only need to verify once. Call [Get Rate](/api-reference/endpoint/v3/rate/get-rate) with `fromCurrency=NGN` and `toCurrency=USDT` (or your desired fiat/crypto). Save the `key` from the response. ```bash theme={null} curl --request GET \ --url 'https://api.getpartna.com/v4/rate?fromCurrency=NGN&toCurrency=USDT&fromAmount=10000' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' ``` Call [Onramp and Offramp](/api-reference/endpoint/v3/ramp/onramp-and-offramp) with `type: "fiatToCrypto"`. Include the rate key, amounts, currency pair, and the user's crypto wallet address. ```bash theme={null} curl --request POST \ --url https://api.getpartna.com/v4/ramp \ --header 'Content-Type: application/json' \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_USERNAME' \ --data '{ "type": "fiatToCrypto", "fromCurrency": "NGN", "fromNetwork": "naira", "fromAmount": 10000, "toCurrency": "USDT", "toNetwork": "tron", "cryptoAddress": "TXyz123...userWalletAddress", "rateKey": "RATE_KEY_FROM_STEP_3", "rampReference": "your-unique-reference-id" }' ``` The response includes a bank account (`accountName`, `accountNumber`, `bankName`) where the user should send their NGN payment. The user transfers the specified NGN amount to the bank account returned in step 4. This can be done via bank transfer or any supported local payment method. Once Partna confirms the fiat payment, the equivalent crypto is sent to the user's wallet address. A webhook event is dispatched to your endpoint. ## Example response (step 4) ```json theme={null} { "data": { "accountName": "JANE DOE", "accountNumber": "9003001002", "bankName": "Paystack-Titan", "currentRate": 0.000597221725, "discountApplied": "0", "expiryDate": 1755873600, "feeInFromCurrency": 16.74, "feeInToCurrency": 0.01, "fromAmount": 10000, "fromCurrency": "NGN", "fromNetwork": "naira", "rampReference": "f32c3925063447a54905d31305a14da5", "toAmount": 5.872, "toCurrency": "USDT", "toNetwork": "tron", "totalFeesInFromCurrency": 16.74, "totalFeesInToCurrency": 0.01 }, "message": "request completed" } ``` ## Key parameters | Parameter | Description | | -------------------------- | ------------------------------------------------------------------------------------- | | `type` | Must be `fiatToCrypto` for onramp | | `fromCurrency` | The local currency the user is paying in (e.g., `NGN`) | | `fromNetwork` | The payment network for the local currency (e.g., `naira`, `mobileMoney`, `mpesa`) | | `toCurrency` | The crypto the user will receive (e.g., `USDT`, `USDC`, `BTC`) | | `toNetwork` | The blockchain network for delivery (e.g., `tron`, `ethereum`, `solana`) | | `cryptoAddress` | The user's wallet address to receive crypto | | `rateKey` | The rate key from [Get Rate](/api-reference/endpoint/v3/rate/get-rate) | | `rampReference` | Your unique reference for this transaction (use for idempotency) | | `fromAmount` | The amount in local currency | | `cancelPendingRampRequest` | Set to `true` to cancel any existing pending request for this user. Default: `false`. | | `expireAction` | What happens if the rate expires: `useCurrentRate` (default) or `deposit` | | `rampExpTime` | Custom expiration time in seconds. Default: 1 hour. | ## Handling pending requests Each user can only have one active ramp request at a time. If you need to create a new request while one is pending, set `cancelPendingRampRequest: true` in your request body. ## Tracking status Use [Get Ramp Requests](/api-reference/endpoint/v3/ramp/get-ramp-requests) to check the status of a ramp request. You will also receive webhook notifications as the transaction progresses through each stage. # Onramp Widget Source: https://docs.getpartna.com/v4/documentation/guides/onramp-widget Redirect users to Partna's onramp widget The onramp widget lets you send users to a Partna-managed checkout where they can pay in local currency and receive crypto in their wallet. Use this flow when you want a faster integration without building your own payment UI. ## Base URLs | Environment | URL | | ------------ | ------------------------------------------------- | | `staging` | `https://staging-pay.getpartna.com/v4/pay/onramp` | | `production` | `https://pay.getpartna.com/v4/pay/onramp` | ## Required query parameters | Parameter | Required | Description | | --------------- | -------- | ----------------------------------------------------------------------- | | `amount` | Yes | Amount the user will pay in local currency | | `from_currency` | Yes | Local currency the user is paying with, such as `NGN`, `KES`, or `GHS` | | `to_currency` | Yes | Crypto the user should receive, such as `USDT`, `USDC`, `BTC`, or `ETH` | | `to_network` | Yes | Network to deliver the crypto on, such as `tron`, `celo`, or `ethereum` | | `address` | Yes | Wallet address that will receive the crypto | | `merchant` | Yes | Your Partna merchant username | | `reference` | No | A hex string transaction reference | ## Registration modes The widget supports two merchant-controlled registration modes: | Mode | Behavior | | -------- | ---------------------------------------------------------------------------------- | | `open` | Users can begin registration through the widget flow and complete onboarding later | | `closed` | Only users you have already created can continue in the widget flow | You can manage this setting through [Update Settings](/api-reference/endpoint/v3/user/registration-deposit-and-fee-settings) and inspect the current value with [Get Settings](/api-reference/endpoint/v3/user/get-registration-deposit-and-fee-settings). If your registration mode is `open`, you can use [Register Account](/api-reference/endpoint/v3/account/register-account) to create a pending registration. If your registration mode is `closed`, create the user up front with [Create Account](/api-reference/endpoint/v3/account/create-account). ## How it works Build a URL using the correct environment URL and the required query parameters. Send the user to the hosted Partna checkout from your app or website. The user follows the payment instructions in the hosted flow and pays the local currency amount. Partna sends `Onramp` webhook events as the transaction moves through each state. See [Webhooks](/v4/documentation/core-concepts/webhooks) for signature verification and endpoint setup. Once the transaction reaches `completed`, the crypto has been sent to the destination wallet address. ## Build the redirect URL ```typescript TypeScript theme={null} const redirectURL = environment === "staging" ? "https://staging-pay.getpartna.com/v4/pay/onramp" : "https://pay.getpartna.com/v4/pay/onramp"; const params = new URLSearchParams(); params.append("from_currency", fromCurrency); params.append("to_currency", toCurrency); params.append("to_network", toNetwork); params.append("amount", amount); params.append("address", walletAddress); params.append("merchant", merchant); if (reference) { params.append("reference", reference); } const url = `${redirectURL}?${params.toString()}`; window.location.href = url; ``` ```javascript JavaScript theme={null} const redirectURL = environment === "staging" ? "https://staging-pay.getpartna.com/v4/pay/onramp" : "https://pay.getpartna.com/v4/pay/onramp"; const params = new URLSearchParams({ from_currency: fromCurrency, to_currency: toCurrency, to_network: toNetwork, amount, address: walletAddress, merchant, }); if (reference) { params.append("reference", reference); } window.location.href = `${redirectURL}?${params.toString()}`; ``` ## Example redirect URL ```text theme={null} https://pay.getpartna.com/v4/pay/onramp?amount=1000&from_currency=KES&to_currency=CUSD&to_network=celo&address=0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d&merchant=your-merchant-username&reference=a152225b9ccd6d28c2b58f8828a81125 ``` For staging, use `https://staging-pay.getpartna.com/v4/pay/onramp` instead. ## Transaction lifecycle Partna emits `Onramp` webhooks with these statuses: | Status | Meaning | | ------------ | ---------------------------------------------------------------- | | `pending` | The onramp request has been created and is awaiting user payment | | `received` | Partna has received the user's payment | | `processing` | Conversion or transfer is currently being processed | | `completed` | The crypto delivery is complete | ## Sample webhook payloads ### `pending` ```json theme={null} { "event": "Onramp", "data": { "transactionReference": "a152225b9ccd6d28c2b58f8828a81125", "status": "pending", "timestamp": 1773760151, "fromAmount": 14.2, "toAmount": 0.0954, "fromCurrency": "KES", "toCurrency": "CUSD", "fromNetwork": "kenyanshilling", "toNetwork": "celo", "totalFeesInFromCurrency": 1.8, "totalFeesInToCurrency": 0.0138, "expireAt": 1773763751, "toExternalAccount": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d" }, "signature": "BASE64_SIGNATURE" } ``` ### `received` ```json theme={null} { "event": "Onramp", "data": { "transactionReference": "a152225b9ccd6d28c2b58f8828a81125", "status": "received", "timestamp": 1773760193, "fromAmount": 14.2, "toAmount": 0.095407692303476, "fromCurrency": "KES", "toCurrency": "CUSD", "totalFeesInFromCurrency": 1.797000000052, "totalFeesInToCurrency": 0.013823076922924, "toExternalAccount": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d" }, "signature": "BASE64_SIGNATURE" } ``` ### `processing` ```json theme={null} { "event": "Onramp", "data": { "transactionReference": "a152225b9ccd6d28c2b58f8828a81125", "status": "processing", "timestamp": 1773760502, "transactionHash": "0xc41e313cfb60826d8ccb1803f55fdaaa8d72c269b35fc1a273f7fe3bd7b86831", "fromAmount": 14.2, "toAmount": 0.0954, "fromCurrency": "KES", "toCurrency": "CUSD", "fromNetwork": "kenyanshilling", "toNetwork": "celo", "expireAt": 1773763751, "toExternalAccount": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d" }, "signature": "BASE64_SIGNATURE" } ``` ### `completed` ```json theme={null} { "event": "Onramp", "data": { "transactionReference": "a152225b9ccd6d28c2b58f8828a81125", "status": "completed", "timestamp": 1773760502, "transactionHash": "0xc41e313cfb60826d8ccb1803f55fdaaa8d72c269b35fc1a273f7fe3bd7b86831", "fromAmount": 14.2, "toAmount": 0.0954, "fromCurrency": "KES", "toCurrency": "CUSD", "fromNetwork": "kenyanshilling", "toNetwork": "celo", "toExternalAccount": "0x00B6845c6F47C770cE630B96df9BD4A6dA91C65d" }, "signature": "BASE64_SIGNATURE" } ``` ## When to use the hosted widget Use the hosted widget when you want: * A faster integration with less frontend work * Partna to handle the payment instructions UI * A simple redirect-based flow from your product Use the API-based [Onramp Guide](/v4/documentation/guides/onramp) when you need full control over the user experience and payment orchestration. # Introduction Source: https://docs.getpartna.com/v4/documentation/introduction Stablecoin-powered payment infrastructure for Africa Partna is a payment API that connects African currencies to the global financial system through stablecoins. Collect local currencies, convert between fiat and crypto, and settle in USD or stablecoins. ## What you can build **Onramp**: Your users pay in local currency (NGN, GHS, KES). They receive USDT, USDC, or other supported crypto in their wallet. The conversion, compliance, and settlement are handled by Partna. **Offramp**: Your users send crypto. They receive local currency in their bank account or mobile money account. Partna handles the conversion and payout automatically. **Collect and settle**: Collect payments from African customers in local currency. Settle in USD or stablecoins. No more waiting months for cross-border banking rails to move your money. **Payout**: Send local currency to bank accounts or mobile money wallets across supported markets. Fund your payout balance with USD, stablecoins, or local currency. ## Supported markets | Market | Currency | Status | | ------- | -------- | --------- | | Nigeria | NGN | Live | | Kenya | KES | Live | | Ghana | GHS | Launching | | Malawi | MWK | Launching | | Zambia | ZMW | Launching | ## Supported assets You can retrieve the list of supported assets, by network and by currency, for each environment via the [GET supported assets endpoint](/api-reference/endpoint/v3/supported/get-supported-assets). ## How it works Partna routes payments through stablecoin rails to bypass the traditional banking bottlenecks that make cross-border settlement slow and expensive. Your customers pay in local currency. You receive USD or stablecoins. Your users never need to touch or understand crypto unless you want them to. ## Trusted by Minipay (1M+ users, 99% transaction success rates), Cenoa, Grabrfi, Hyperverge, Onramp Money, and Bitoshi. Over 200,000 transactions processed monthly with \$4M+ in monthly volume. ## Next steps Make your first API call in under 5 minutes. Get your API credentials and start building. Let your users convert local currency to crypto. Let your users convert crypto to local currency. # Quickstart Source: https://docs.getpartna.com/v4/documentation/quickstart Make your first API call in under 5 minutes This guide walks you through making your first Partna API call. By the end, you will understand the basic request pattern for every endpoint. ## 1. Get your API credentials Fill out the [onboarding form](https://forms.getpartna.com/get-started). The Partna team will reach out to schedule a brief onboarding call and set up your merchant account. Once your merchant account is set up, log in to the Partna dashboard, set up 2fa and generate your API keys. You will get separate keys for **staging** and **production**: * Staging dashboard: [staging-dashboard.getpartna.com](https://staging-dashboard.getpartna.com/settings?tab=api-webhooks) * Production dashboard: [dashboard.getpartna.com](https://dashboard.getpartna.com/settings?tab=api-webhooks) Take note of your created **API Key** and **Merchant Username**. You will need to include these as the `x-api-key` and `x-api-user` headers in every authenticated request. Build and test against the staging environment before deploying to production. Staging supports [mock deposits](https://staging-dashboard.getpartna.com/mock/deposit) and test transactions at no cost. ## 2. Make your first request Fetch your account details. This is the simplest authenticated request to confirm your credentials work and to see your available balances. ```bash cURL theme={null} curl --request GET \ --url https://api.getpartna.com/v4/account \ --header 'x-api-key: YOUR_API_KEY' \ --header 'x-api-user: YOUR_MERCHANT_USERNAME' ``` ```python Python theme={null} import requests response = requests.get( "https://api.getpartna.com/v4/account", headers={ "x-api-key": "YOUR_API_KEY", "x-api-user": "YOUR_MERCHANT_USERNAME" } ) print(response.json()) ``` ```javascript Node.js theme={null} const response = await fetch("https://api.getpartna.com/v4/account", { headers: { "x-api-key": "YOUR_API_KEY", "x-api-user": "YOUR_MERCHANT_USERNAME" } }); const data = await response.json(); console.log(data); ``` ## 3. Check the response A successful response returns your account balances across all supported currencies and their underlying assets and asset IDs (wallet addresses). Once you confirm you can fetch your account details, you are ready to start using other Partna API endpoints. ```json theme={null} { "data": { "accounts": [ { "BNB": { "accountName": "user1", "asset": [{ "id": "0x20B3...", "meta": null, "type": "crypto" }], "balance": 0, "pendingDebit": 0 }, "BTC": { "accountName": "user1", "asset": [{ "id": "tb1q6z...", "meta": null, "type": "crypto" }], "balance": 0, "pendingDebit": 0 }, "ETH": { "accountName": "user1", "asset": [{ "id": "0x20B3...", "meta": null, "type": "crypto" }], "balance": 0, "pendingDebit": 0 }, "GHS": { "accountName": "user1", "asset": [{ "id": "", "meta": null, "type": "fiat" }], "balance": 0, "pendingDebit": 0 }, "KES": { "accountName": "user1", "asset": [{ "id": "", "meta": null, "type": "fiat" }], "balance": 0, "pendingDebit": 0 }, "NGN": { "accountName": "user1", "asset": [{ "id": "", "meta": null, "type": "fiat" }], "balance": 0, "pendingDebit": 0 }, "USD": { "accountName": "user1", "asset": [ { "id": "0x20B3...", "meta": null, "type": "crypto" }, { "id": "TCx7wx...", "meta": null, "type": "crypto" }, { "id": "EXq68w...", "meta": null, "type": "crypto" } ], "balance": 0, "pendingDebit": 0 } } ] }, "message": "success" } ``` ### Understanding Asset IDs The `asset` array within each currency object contains an id. For crypto assets, the `id` is the wallet address for that specific network. To find the correct Asset ID for a specific currency and network combo, use the indexing data from the [Supported Assets](/v4/documentation/introduction#supported-assets) response: ```javascript JavaScript theme={null} // 1. Get destination details from Supported Assets response const assetInfo = getAssetResponse.byCurrency[selectedCurrency][selectedNetwork]; const { destinationCurrency, assetIndex } = assetInfo; // 2. Use those details to find the correct asset in your Get Account response const assetId = getAccountResponse.data.accounts[destinationCurrency].asset[assetIndex].id; ``` ```python Python theme={null} # 1. Get destination details from Supported Assets response asset_info = get_asset_response['byCurrency'][selected_currency][selected_network] destination_currency = asset_info['destinationCurrency'] asset_index = asset_info['assetIndex'] # 2. Use those details to find the correct asset in your Get Account response asset_id = get_account_response['data']['accounts'][destination_currency]['asset'][asset_index]['id'] ``` This logic ensures you always use the correct wallet address for the network and currency combination you have selected. ## 4. Set up your webhook endpoint Before processing real transactions, configure a webhook URL to receive status updates. You can do this via the [dashboard](https://dashboard.getpartna.com/settings?tab=api-webhooks) or the [Update Webhook URL](/api-reference/endpoint/v3/user/update-webhook-url) endpoint. Your webhook endpoint should return a `200` status code immediately upon receiving the event. Process the event asynchronously. ## Next steps You are now authenticated and can fetch rates. Here is what to build next, depending on your use case: | Use case | Guide | | ---------------------------------------------------------------- | ----------------------------------------------------------------------- | | Users convert local currency to crypto | [Onramp Guide](/v4/documentation/guides/onramp) | | Users convert local currency to crypto with a Partna checkout | [Onramp Widget Guide](/v4/documentation/guides/onramp-widget) | | Users convert crypto to local currency | [Offramp Guide](/v4/documentation/guides/offramp) | | Users convert crypto to local currency with a Partna payout flow | [Offramp Widget Guide](/v4/documentation/guides/offramp-widget) | | Collect payments and settle in USD/stablecoins | [Collect and Settle Guide](/v4/documentation/guides/collect-and-settle) | | Verify user identity for transactions | [Accounts and KYC Guide](/v4/documentation/guides/accounts-and-kyc) | | Test with mock transactions in staging | [Testing Guide](/v4/documentation/core-concepts/testing) |