Search documentation...

Introduction

Welcome to the Milta Wallet Partner API documentation. This API allows partners to integrate wallet, transaction, and payment services. Our REST API provides comprehensive endpoints for managing transfers, account balances, pricing estimates, and reference data. Built with developers in mind, our API is designed to be intuitive, consistent, and powerful.

Base URL

https://api.dev.wallet.milta.be/api/v1/partner

All API endpoints are relative to this base path

RESTful Design

Predictable resource-oriented URLs and standard HTTP methods

JSON Everywhere

All requests and responses use JSON with UTF-8 encoding

Comprehensive Errors

Detailed error messages with codes for easy debugging purposes.

Authentication

All API requests require HTTP Basic Authentication. Use your API Key ID as the username and API Key Secret as the password.

⚠️ Keep your credentials secure

Never expose your API Key ID or Secret in client-side code, public repositories, or version control. Treat them like passwords and rotate them regularly.

Basic Authentication

Most HTTP clients support Basic Auth natively. The credentials are sent in the Authorization header as a Base64-encoded string.

CredentialDescription
UsernameYour API Key ID
PasswordYour API Key Secret

Example Requests

# Using cURL with Basic Auth
curl -u "YOUR_API_KEY_ID:YOUR_API_KEY_SECRET" \
  https://api.dev.wallet.milta.be/api/v1/partner/inventories/categories
// JavaScript/Node.js
const credentials = Buffer.from(`${apiKeyId}:${apiKeySecret}`).toString('base64');

fetch('https://api.dev.wallet.milta.be/api/v1/partner/inventories/categories', {
  headers: {
    'Authorization': `Basic ${credentials}`,
    'Content-Type': 'application/json'
  }
});

Quick Start

Get up and running in minutes with our quick start guide.

1

Create an account

Sign up for free and access your dashboard

2

Generate API key

Create a new API key from the settings page

3

Make your first request

Test the API with a simple GET request

Test with Postman

Import our Postman collection to quickly test API endpoints without writing code. Our collection includes pre-configured requests for all endpoints with example payloads.

Run in Postman

One-click import directly into your Postman workspace.

Setup Instructions

1

Import the Collection

Download and import the JSON file into Postman, or use the "Run in Postman" button for direct import.

2

Configure Environment Variables

Create a new Postman environment and add your API key as API_KEY.

3

Start Testing

Select any request from the collection, review the parameters, and click Send to test the endpoint.

Pro Tip: The collection includes pre-request scripts that automatically generate timestamps and request IDs. Use the built-in tests to validate responses.

Terminology

Understanding key terms used throughout our API documentation will help you integrate more effectively.

Transfer

A financial transaction that moves funds from your account to a recipient. Each transfer has a unique ID and can be tracked throughout its lifecycle.

Provider

A payment network or financial institution that processes transactions. Different providers may have varying fee structures, processing times, and geographic coverage.

Product

A specific service offering such as mobile top-ups, bill payments, or money transfers. Each product has unique parameters and availability constraints.

Promotion

Special offers or discounts applied to transactions. Promotions may be time-limited, region-specific, or tied to particular products.

API Responses

All API responses follow a consistent JSON structure with standard HTTP status codes.

Success Response Format

{
  "status": "success",
  "data": {
    // Response payload specific to the endpoint
  }
}

Error Response Format

{
  "status": "error",
  "message": "Error description with details about what went wrong"
}

HTTP Status Codes

CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request parameters
401UnauthorizedInvalid or missing API key
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error - retry with backoff

Error Codes

Comprehensive list of error codes you may encounter when using the API.

Error CodeDescriptionResolution
INVALID_API_KEYAPI key is missing or invalidCheck your API key in dashboard
INSUFFICIENT_FUNDSAccount balance too lowAdd funds to your account
INVALID_RECIPIENTRecipient details are incorrect or not foundVerify recipient phone number and country code
PRODUCT_NOT_AVAILABLERequested product is unavailable in this regionCheck product availability via the inventories endpoint
RATE_LIMIT_EXCEEDEDToo many requests in a short time periodImplement exponential backoff and retry
TRANSFER_ALREADY_EXISTSA transfer with this external ID already existsUse a unique externalId per request
INVALID_AMOUNTTransfer amount is outside the allowed rangeCheck min/max amounts via the pricing endpoint
PROVIDER_UNAVAILABLEThe payment provider is temporarily unavailableRetry after a short delay or use an alternative provider
INTERNAL_ERRORUnexpected server-side error occurredRetry with exponential backoff; contact support if persists

Method Usage Limits

Rate limits are enforced per API key to ensure fair usage and platform stability. When a limit is exceeded, the API responds with HTTP 429 Too Many Requests.

EndpointRate LimitNotes
GET /inventories/*300 req/minReference data endpoints
POST /transfers60 req/minPer API key
GET /transfers/*300 req/minTransfer status lookups
GET /balance60 req/minAccount balance queries
POST /pricing120 req/minPrice estimation calls

Handling 429 Errors

// Retry with exponential backoff
async function requestWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (err.status === 429 && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw err;
      }
    }
  }
}
Note: Rate limit windows reset every 60 seconds. The response headers include X-RateLimit-Remaining and X-RateLimit-Reset to help you manage your request pace.

Querying

List endpoints support query parameters to filter and refine results. Parameters are passed as URL query strings and are always optional unless stated otherwise.

ParameterTypeDescription
countrystringISO 3166-1 alpha-2 country code (e.g. GB, US)
currencystringISO 4217 currency code (e.g. GBP, USD)
providerIdstringFilter results to a specific provider
productTypestringFilter by product category

Example

GET /inventories/products?country=GB&currency=GBP&productType=TOPUP

Authorization: Basic {base64(apiKeyId:apiKeySecret)}

Paging

Endpoints that return collections support pagination to manage large result sets. Use the page and pageSize query parameters to navigate through results.

ParameterTypeDescription
pageintegerPage number (1-based). Defaults to 1.
pageSizeintegerNumber of items per page. Defaults to 20, max 100.

Paginated Response

{
  "status": "success",
  "data": {
    "items": [ ... ],
    "pagination": {
      "page": 1,
      "pageSize": 20,
      "totalItems": 84,
      "totalPages": 5
    }
  }
}

Batching

Submit multiple transfer requests in a single API call using the batch endpoint. Batching reduces network overhead and simplifies bulk operations.

Batch Limits

Max transfers per batch

A single batch request may contain up to 100 transfers. Each transfer within the batch is processed independently — a failure in one does not affect others.

Batch Request

POST /transfers/batch

{
  "transfers": [
    {
      "externalId": "TXN-001",
      "productId": "PRODUCT_123",
      "recipientPhone": "+447700900001",
      "amount": 10.00,
      "currency": "GBP"
    },
    {
      "externalId": "TXN-002",
      "productId": "PRODUCT_456",
      "recipientPhone": "+447700900002",
      "amount": 5.00,
      "currency": "GBP"
    }
  ]
}

Batch Response

{
  "status": "success",
  "data": {
    "batchId": "BATCH-20260101-ABC",
    "accepted": 2,
    "rejected": 0,
    "transfers": [
      { "externalId": "TXN-001", "status": "PENDING", "transferId": "T-9001" },
      { "externalId": "TXN-002", "status": "PENDING", "transferId": "T-9002" }
    ]
  }
}
Note: Each transfer in a batch must have a unique externalId. Duplicate IDs within the same batch will cause those entries to be rejected.

Localization

The API supports locale-aware responses. Pass localization headers to receive product names, descriptions, and messages in the preferred language and currency format.

HeaderExampleDescription
Accept-Languageen-GBPreferred language for response messages
X-CurrencyGBPPreferred display currency (ISO 4217)
X-CountryGBTarget country context (ISO 3166-1 alpha-2)

Example

GET /inventories/products
Accept-Language: fr-FR
X-Currency: EUR
X-Country: FR
Authorization: Basic {base64(apiKeyId:apiKeySecret)}

Reference Data

Reference data endpoints provide static or slowly-changing information about supported countries, currencies, providers, and products. Cache these responses to reduce API calls.

MethodEndpointDescription
GET/inventories/countriesList all supported countries
GET/inventories/currenciesList all supported currencies
GET/inventories/categoriesList product categories
GET/inventories/providersList available payment providers
GET/inventories/productsList products with optional filters

Example Response

GET /inventories/countries

{
  "status": "success",
  "data": {
    "items": [
      { "code": "GB", "name": "United Kingdom", "currencyCode": "GBP" },
      { "code": "FR", "name": "France", "currencyCode": "EUR" },
      { "code": "NG", "name": "Nigeria", "currencyCode": "NGN" }
    ]
  }
}

Deferred SendTransfer

Schedule transfers for future execution using the deferred transfer feature. This allows you to prepare transfers in advance and have them execute automatically at the specified time.

How It Works

1

Submit a deferred transfer

Include `deferred: true` and a `scheduledAt` timestamp in your POST /transfers request.

2

Transfer enters SCHEDULED state

The API acknowledges the request and holds the transfer until the scheduled time.

3

Automatic execution

At the scheduled time, the platform processes the transfer and updates its status to PENDING or COMPLETED.

Request Example

POST /transfers

{
  "externalId": "TXN-20260101-001",
  "productId": "PRODUCT_123",
  "recipientPhone": "+447700900001",
  "amount": 10.00,
  "currency": "GBP",
  "deferred": true,
  "scheduledAt": "2026-01-15T09:00:00Z"
}

Check Status

GET /transfers/T-9001

{
  "status": "success",
  "data": {
    "transferId": "T-9001",
    "externalId": "TXN-20260101-001",
    "status": "SCHEDULED",
    "scheduledAt": "2026-01-15T09:00:00Z",
    "amount": 10.00,
    "currency": "GBP"
  }
}

⚠️ Cancellation window

Deferred transfers can be cancelled up to 5 minutes before their scheduled time by calling DELETE /transfers/{id}. After that window, cancellation is not guaranteed.

Optional Features

The Milta Partner API offers several optional features that can be enabled to enhance your integration. Contact your account manager to enable features marked "Available on request".

Webhook Notifications

Available on request

Receive real-time HTTP callbacks when transfer statuses change. Configure your webhook URL in the partner dashboard to avoid polling the status endpoint.

IP Allowlisting

Available on request

Restrict API access to specific IP addresses or CIDR ranges for additional security. Configurable per API key from the developer settings panel.

Idempotency Keys

Recommended

Pass an X-Idempotency-Key header to safely retry requests without risk of duplicate transfers. Keys are valid for 24 hours.

Sandbox Environment

Available by default

A full sandbox environment mirrors the production API for end-to-end testing without real money movement. Use test credentials from your dashboard.

Need something custom? Contact your Milta account manager or reach out via the partner dashboard to discuss feature requirements tailored to your integration.