Browse a priced catalogue, place an order, get the decision on a webhook. We handle the documents, the consulate and the paperwork behind them — you keep the customer, the brand and the margin.
curl https://api.musafir.com/b2b/v1/visa/AE \
-H "Authorization: Bearer $MUSAFIR_TOKEN"{
"destination": "AE",
"currency": "AED",
"options": [
{
"skuId": "665f7c1e9a3b4d2f18c07a91",
"title": "Tourist · 30 days · single entry",
"price": 349.00,
"processing": "Standard",
"estimatedDays": [3, 5],
"documents": ["PASSPORT", "PHOTO", "RETURN_TICKET"],
"bookableOnline": true
}
]
}Not because the product is complicated — because the operation behind it is. That operation is the thing we are handing you.
Eligibility, fees and document lists change by nationality, by residency and by consulate — and they change without notice. Keeping a visa rulebook current is a full-time operation, not a feature.
Chasing passport scans over email is where an international booking quietly dies. Every day between intent and submission is a cancellation waiting to happen.
Once an application leaves your hands you have nothing to tell the traveller — until they call. Support absorbs a cost that should never have existed.
That is the integration. Not a phase-one, not a pilot scope — the whole thing. Everything that makes visas hard is on our side of the line.
import { Musafir } from "@musafir/visa";
const musafir = new Musafir(process.env.MUSAFIR_API_KEY);
// 1. What can this traveller actually get?
const { options } = await musafir.visas.list({
destination: "AE",
nationality: "IN",
residency: "AE",
});
// 2. Sell it at your price. We invoice you, not your customer.
const order = await musafir.orders.create({
skuId: options[0].skuId,
traveller,
});
// 3. We take it from here.
musafir.on("order.decided", ({ orderId, outcome, documentUrl }) => {
notifyCustomer(orderId, outcome, documentUrl);
});Eligibility, fees and document lists differ by nationality, residency and consulate, and they move without notice. That is our problem, and the answer arrives in the catalogue response.
Upload what the traveller gives you and we validate it against what the consulate will actually accept — before it is submitted, not after it is rejected.
Orders draw against a credit line and settle on invoice. You never handle a visa fee, so the API adds nothing to your PCI scope.
This is our live catalogue — the same eligibility rules, prices and document lists running musafir.com right now. Pick a nationality, a residency and a destination, and see exactly what a partner would get back. Some combinations lose a visa entirely; some change the paperwork. That is the work you are handing us.
Ask for a destination and get back the options that traveller is actually eligible for — with your contracted fee, the processing tier, and the exact document list the consulate will ask for.
curl https://api.musafir.com/b2b/v1/visa/AE \
-H "Authorization: Bearer $MUSAFIR_TOKEN"{
"destination": "AE",
"currency": "AED",
"options": [
{
"skuId": "665f7c1e9a3b4d2f18c07a91",
"title": "Tourist · 30 days · single entry",
"price": 349.00,
"processing": "Standard",
"estimatedDays": [3, 5],
"documents": ["PASSPORT", "PHOTO", "RETURN_TICKET"],
"bookableOnline": true
}
]
}Create the order, attach travellers and documents, submit. There is no hosted checkout to redirect into and no card to collect — the order books against your account and settles on invoice.
curl -X POST https://api.musafir.com/b2b/v1/orders \
-H "Authorization: Bearer $MUSAFIR_TOKEN" \
-H "Idempotency-Key: 8f14e45f-ea2b-4c1d-9f3a-77b1c0d2e9a4" \
-d '{
"skuId": "665f7c1e9a3b4d2f18c07a91",
"nationality": "IN",
"residency": "AE",
"partnerCustomerRef": "BOOKING-91882",
"contact": {
"fullName": "Aarav Sharma",
"email": "aarav@example.com"
}
}'{
"orderId": "ord_2f9c41a7",
"reference": "VIS-Q8NN6J",
"status": "Draft",
"nextActions": ["travelers", "documents", "submit"],
"total": {
"amount": 349.00,
"currency": "AED"
}
}Status is pushed, not polled. Every transition fires a signed webhook, and when the authority decides, the outcome and any issued document land on your endpoint.
POST https://your-app.example.com/webhooks/musafir
X-Musafir-Signature: t=1752480000,v1=8a4f2c...{
"event": "order.decided",
"orderId": "ord_2f9c41a7",
"occurredAt": "2026-07-14T09:12:04Z",
"travelers": [
{
"travelerId": "trv_1",
"outcome": "Approved",
"decidedBy": "UAE ICP",
"document": {
"validUntil": "2026-11-30",
"documentUrl": "https://api.musafir.com/b2b/v1/orders/ord_2f9c41a7/travelers/trv_1/document"
}
}
]
}Musafir is a licensed visa processing agent. We prepare, submit and track applications with the issuing authority. The decision — approve, reject, ask for more — belongs to that government alone, and no API changes that. What you can promise your customer is a correct application, submitted quickly, tracked to a decision. Not the decision itself.
This is what we are building for pilot partners — not something you can sign up for this afternoon. Reserved test inputs let you force an approval, a rejection or a documents-required loop on demand, so you can write the unhappy paths without waiting on a consulate to reject something for you.
Two minutes on the form below. We reply within two business days — with where things actually stand, not a brochure.
Pilot partners get the full API contract and shape the parts that don't fit yet, while they still can.
When the sandbox opens, pilot partners get the keys before it is announced to anyone else.
Force any outcome you need to handle, on demand — so you can write the rejection path without waiting on a consulate to reject something for you.
| Sandbox input | What it forces |
|---|---|
Passport marked APPROVE | Order is decided as approved and the webhook fires |
Passport marked REJECT | Order is decided as rejected, your account is credited back |
Passport marked DOCS | Documents-required loop, back to Draft |
SKU prefixed SLOW- | Delayed decision, for testing your retry and timeout paths |
Idempotency-Key: replay-* | Returns the first order instead of creating a duplicate |
Amount over the sandbox limit | 402 over credit limit |
Sandbox tokens will be rejected by production and production tokens by the sandbox, so a test order can never touch a real one. Traveller comms stay suppressed; webhooks do not.
Stop couriering passports and queueing at typing centres. Take the application at the counter and let us carry it to the consulate.
Attach a visa to every international booking. It is the highest-margin ancillary you are not currently selling.
Surface the document requirement before check-in, not at the gate. Fewer denied boardings, one less INAD fine.
Corporate travellers tracked to a decision, with the policy controls and reporting your clients already expect.
Including the ones we would rather you didn’t ask. If something here isn’t straight enough, tell us and we’ll fix the answer.
We are choosing a small group of pilot partners to build this with. Tell us what you would sell and we will show you the contract while it can still change.
This is our live catalogue — the same eligibility rules, prices and document lists running musafir.com right now. Pick a nationality, a residency and a destination, and see exactly what a partner would get back. Some combinations lose a visa entirely; some change the paperwork. That is the work you are handing us.
Business Sticker Visa
Standard · 7–8 days
Tourist Sticker Visa
Standard · 7–8 days
Change the nationality and watch this list change. Keeping it correct, per profile, per consulate, is the job you are handing us.
curl "https://api.musafir.com/b2b/v1/visa/DZ?nationality=IN&residency=AE" \
-H "Authorization: Bearer $MUSAFIR_TOKEN"{
"destination": "DZ",
"nationality": "IN",
"residency": "AE",
"visaRequired": true,
"currency": "AED",
"options": [
{
"skuId": "019ed11f-b573-7558-832e-d48b3906a6e8",
"title": "Business Sticker Visa",
"price": 195,
"processing": "Standard",
"estimatedDays": [
7,
8
],
"bookableOnline": true
},
{
"skuId": "019ed11f-a311-7011-a5d5-d9e76ffc7032",
"title": "Tourist Sticker Visa",
"price": 195,
"processing": "Standard",
"estimatedDays": [
7,
8
],
"bookableOnline": true
}
],
"documents": [
"PASSPORT_FIRST_PAGE",
"LAST_PASSPORT",
"RESIDENCY_VISA",
"RESIDENCY_ID",
"INVITATION_LETTER",
"FLIGHT_TICKET",
"HOTEL_BOOKING",
"QUESTIONNAIRE"
]
}Real answers from our live catalogue, shaped into the B2B response we intend to ship. The B2B endpoint itself isn't live yet — this is what it will return.