Search a priced catalogue, create the whole party in one call, attach documents, submit. We handle the consulate and tell you when the decision lands. You keep the customer, the brand and the margin.
curl -X POST "$MUSAFIR_VISA_API/v1/catalogue/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination": "AE",
"travelDate": "2026-11-20",
"travellers": [
{ "ref": "t1", "type": "ADT", "nationality": "IN", "residency": "AE" }
]
}'{
"destination": { "code": "AE", "name": "United Arab Emirates" },
"advisory": null,
"products": [
{
"productId": "AE_EV_ADT",
"title": "UAE 30 Day Tourist Visa",
"bookable": true,
"processingDays": 4,
"supportedTravellerTypes": ["ADT", "CHD"],
"pricing": { "amount": 380, "currency": "AED", "firm": false }
}
]
}A travel visa API lets travel agencies, airlines, OTAs and travel platforms offer visa services inside their own website or app. It returns visa options, requirements and pricing for a given traveller, creates applications, accepts documents and reports application status, without the platform building its own visa processing operation.
The Musafir Visa API is a partner interface to Musafir's visa processing platform, which has handled over 1.6 million visa applications and runs visa services on musafir.com today. Partners search a priced catalogue, create an order for the whole party, upload documents, submit, and receive decisions by webhook, while Musafir handles documents and consulate processing. It is published for developers as the JETT Visa Partner API, with the full reference at developers.jett.travel. The API is in private beta and credentials are issued per partner.
| Who it is for | Travel agencies, OTAs, airlines, TMCs and tour operators |
|---|---|
| Integration | REST, JSON. Six endpoints from catalogue to submitted application |
| Authentication | Client credentials. Scoped bearer token, one hour, no refresh |
| Scopes | catalogue:read, orders:write, orders:read. Request a narrower token any time |
| Catalogue | Priced per partner, resolved by nationality, residency and travel date |
| Eligibility rules | Answered per traveller, so you can name which one is the problem |
| Documents | Per-product checklists with guideline and template files. Uploads to 10 MB |
| Orders | The whole party in one call, passports included. No partial orders |
| Idempotency | Required on every write. A replayed key returns the first response |
| Status | waitingOn says whose court the ball is in, on every order read |
| Webhooks | Action required, decision, status change, order completed or cancelled |
| Settlement | Statement per period, totalled per currency and never converted |
| Sandbox | Its own environment, reserved test inputs, and a status mover for decisions |
| Branding | White-label. The partner keeps the customer and the brand |
| Also published as | JETT Visa Partner API, the same product for developers |
| Documentation | Published in full at developers.jett.travel |
| Availability | Private beta. Credentials issued per partner |
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.
Token, catalogue, requirements, order, documents, submit. That is the integration. Everything that makes visas hard sits on our side of the line.
# 0. A token. One hour, no refresh, scopes ride inside it.
curl -X POST "$MUSAFIR_VISA_API/v1/auth/token" \
-H "Content-Type: application/json" \
-d '{"clientId":"...","clientSecret":"..."}'
# 1. What can this party buy, and what does it cost you?
curl -X POST "$MUSAFIR_VISA_API/v1/catalogue/search" \
-H "Authorization: Bearer $TOKEN" \
-d '{"destination":"AE","travelDate":"2026-11-20",
"travellers":[{"ref":"t1","type":"ADT","nationality":"IN"}]}'
# 2. Create the whole party in one call. Passports ride along.
curl -X POST "$MUSAFIR_VISA_API/v1/orders" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 8f14e45f-ea2b-4c1d-9f3a-77b1c0d2e9a4" \
-d @order.json
# 3. Attach each document, then submit.
curl -X POST "$MUSAFIR_VISA_API/v1/orders/$ORDER/travellers/t1/documents/PASSPORT" \
-H "Authorization: Bearer $TOKEN" -F "file=@passport.jpg"
curl -X POST "$MUSAFIR_VISA_API/v1/orders/$ORDER/submit" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 2c7a91be-5f30-4d18-b0e2-6a4d8c1f7b93"
# 4. We take it from here. The decision arrives on your webhook.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.
Requirements are published per traveller, per product, with the guideline and template files attached. Build your collection screen from the response instead of a list you have to keep current.
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 what a partner gets back. Some combinations lose a visa entirely. Some change the paperwork. That is the work you are handing us.
Priced options, processing tiers and the document list for this consulate.
Transit E-Visa
Standard · 4–5 days
Transit E-Visa
Standard · 4–5 days
Tourist E-Visa
Standard · 4–5 days
Tourist E-Visa
Standard · 4–5 days
Uploaded to us
Change the nationality and watch this list change. Keeping it correct, per profile, per consulate, is the job you are handing us.
curl -X POST "$MUSAFIR_VISA_API/v1/catalogue/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination": "AE",
"travellers": [
{ "ref": "t1", "type": "ADT", "nationality": "IN", "residency": "IN" }
]
}'{
"destination": "AE",
"nationality": "IN",
"residency": "IN",
"visaRequired": true,
"currency": "INR",
"options": [
{
"skuId": "019f933e-536e-7507-a8f2-c3b40d03bc91",
"title": "Transit E-Visa",
"price": 3299,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019f933e-cf5c-72e0-ae9a-ae07bca0fc88",
"title": "Transit E-Visa",
"price": 5299,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019ea64a-23a2-71b7-8841-1ad434ab5046",
"title": "Tourist E-Visa",
"price": 7699,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019e8d7b-334a-7652-91fd-eff69bfa61d5",
"title": "Tourist E-Visa",
"price": 7899,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019e8d7b-fae7-7436-892a-f0bc0d03b870",
"title": "Tourist E-Visa",
"price": 11499,
"processing": "Express",
"estimatedDays": [
2,
3
],
"bookableOnline": true
},
{
"skuId": "019ea64d-9e9a-71b9-b1b9-c8e4b24e24ea",
"title": "Tourist E-Visa",
"price": 13999,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019ea64c-9684-7580-9309-138d926319cb",
"title": "Tourist E-Visa",
"price": 14499,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019ea64d-a1ca-75da-8a5a-da71ef00048a",
"title": "Tourist E-Visa",
"price": 14999,
"processing": "Express",
"estimatedDays": [
2,
3
],
"bookableOnline": true
},
{
"skuId": "019ed976-450d-7002-a0f4-6289450c235c",
"title": "Tourist E-Visa",
"price": 15999,
"processing": "Super Express",
"estimatedDays": [
1,
2
],
"bookableOnline": true
},
{
"skuId": "019f89e1-60a1-7442-a618-1e4ff80211c9",
"title": "Tourist E-Visa",
"price": 18999,
"processing": "Instant",
"estimatedDays": [
1,
1
],
"bookableOnline": true
},
{
"skuId": "019ea64d-f61a-72eb-a299-fcc672c7ab81",
"title": "Tourist E-Visa",
"price": 21499,
"processing": "Standard",
"estimatedDays": [
4,
5
],
"bookableOnline": true
},
{
"skuId": "019f5aea-bd9e-71e0-8960-15d65ea6f923",
"title": "Extension E-Visa",
"price": 27099,
"processing": "Standard",
"estimatedDays": [
2,
3
],
"bookableOnline": true
}
],
"documents": [
"PASSPORT_FIRST_PAGE",
"LAST_PASSPORT",
"PASSPORT_EXTERNAL_COVER_PAGE",
"PAN_CARD",
"PHOTO",
"FLIGHT_TICKET",
"HOTEL_BOOKING",
"AD",
"RETURN-FLIGHT_TICKET"
]
}Real answers from our live catalogue, shaped into the partner response. The partner catalogue endpoint is POST /v1/catalogue/search, documented in full at developers.jett.travel.
Post the party and the travel date. Back comes what those travellers are actually eligible for, with your contracted price, the processing time and the document list the consulate will ask for.
curl -X POST "$MUSAFIR_VISA_API/v1/catalogue/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination": "AE",
"travelDate": "2026-11-20",
"travellers": [
{ "ref": "t1", "type": "ADT", "nationality": "IN", "residency": "AE" }
]
}'{
"destination": { "code": One insert holds every traveller, every passport and the consent you captured. 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 "$MUSAFIR_VISA_API/v1/orders" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 8f14e45f-ea2b-4c1d-9f3a-77b1c0d2e9a4" \
-d '{
"destination": "AE",
"productId": "AE_EV_ADT",
"travelDate": "2026-11-20",
"partnerCustomerRef": "BOOKING-99213",
"contact": { "fullName": "Anita Rao", "email": "agent@partner.example" },
"consent": { "obtainedAt": "2026-09-09T09:15:00Z", "noticeVersion": "2026-06" },
"travellers": [
{
"ref": "t1", "type": "ADT",
"givenName": "Anita", "familyName": "Rao",
"dateOfBirth": "1990-04-11", "nationality": "IN", "residency": "AE",
"passport": {
"number": "Z1234567", "expiry": "2031-04-10",
"gender": "F", "placeOfBirth": "Mumbai"
}
}
]
}'Upload each document against the traveller and the document code the requirements call published. Submit runs the same completeness check our own checkout runs, so you find out something is missing before the consulate does.
# One call per document, named by your own traveller ref.
curl -X POST \
"$MUSAFIR_VISA_API/v1/orders/$ORDER/travellers/t1/documents/PASSPORT" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 6d9b2a10-4c77-4f2e-9a1b-3e5c8d7f0a26" \
-F "file=@passport.jpg"
# Then submit the order. Empty body.
curl -X POST "$MUSAFIR_VISA_API/v1/orders/$ORDER/submit" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: 2c7a91be-5f30-4d18-b0e2-6a4d8c1f7b93"Status is pushed, not polled. Every transition fires a signed webhook, and when the authority decides, the outcome lands on your endpoint. The issued visa is then one call away.
The happy path, in status
POST https://your-app.example.com/webhooks/musafir
X-JETT-Signature: t=1757493262,v1=8a4f2c...
X-JETT-Event-Id: 5f2c9a71d8b34e6f9c1a2b3c4d5e6f70{
"eventId": "5f2c9a71d8b34e6f9c1a2b3c4d5e6f70",
"event": "traveller.decision",
"occurredAt"Musafir is a licensed visa processing agent. We prepare, submit and track applications with the issuing authority. The decision, whether that is approve, reject or 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.
Its own environment, its own data, and two controls production does not have. Reserved partnerCustomerRef values make a new order play out a set way, and a status mover walks an order to any status as our team or the embassy would. You can write the rejection path without waiting on a consulate to reject something for you.
It is published in full, endpoint by endpoint. Nothing is held back for a sales call.
developers.jett.travelTwo minutes on the form below. We reply within two business days with where things stand, not a brochure.
Issued per partner, with your sandbox base URL. Production credentials follow once commercial terms are agreed.
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. Read the live list of reserved values from the sandbox rather than hardcoding it: reserved inputs and the status mover.
| Sandbox input | What it forces |
|---|---|
partnerCustomerRef: TEST-APPROVE | The order runs through to approved and the webhook fires |
partnerCustomerRef: TEST-REJECT | The order is refused, so you can build the refusal path |
POST /v1/sandbox/orders/{id}/advance | Walks the order, or one traveller, to any status you name |
Idempotency-Key, replayed | Returns the first response instead of creating a duplicate |
Sandbox tokens are rejected by production and production tokens by the sandbox, so a test order can never touch a real one. The advance and reserved-input routes do not exist in production at all.
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.
Travellers ask their assistant and it reads musafir.com directly — live prices, processing times, document lists and appointment availability. Nothing to build, nothing to sign in to.
The contract is published and the sandbox is built. Tell us what you would sell and we will get you credentials and a call with the engineers who built it.
{
"orderId": "b3f1c2d4e5a6478995ab12cd34ef5678",
"orderReference": "VIS-100482",
"status": "Draft",
"travelDate": "2026-11-20",
"partnerCustomerRef": "BOOKING-99213",
"travellers": [
{
"travellerId": "8a1f4c7e2b9d4a1e8f3c5b7d9e1a2c4b",
"ref": "t1",
"skuId": "AE_EV_ADT_30D",
"type": "ADT",
"fullName": "Anita Rao"
}
]
}{
"code": "conflict",
"message": "This order is not ready to submit.",
"traceId": "683c45d7cfe447a2a526985bb992c238",
"errors": [
{
"field": "travellers[t2]",
"code": "incomplete",
"message": "Traveller 't2' still has required documents outstanding."
}
]
}