Your email is not confirmed yet. Open the confirmation message and verify your email.
Kawenter · Travel agents
Partner API
Server-to-server API for approved travel agents. Authenticate with a ka_live_… API key, quote agent-priced eVisas, create orders, and pay from your agency wallet. Fees and discounts are always computed by Kawenter — never send totals as authoritative input.
This account is not an approved travel agent.
Contact Kawenter support to enable Partner API access.
Keys are shown once when created. Store them securely — Partner API only spends from your agency wallet.
You can have up to 2 active keys.
Default limits per key: 30 requests/minute and 1,000/day,
plus agency-wide caps shared across all keys
(returned in X-RateLimit-* headers).
You already have 2 active keys. Revoke one to create another (Rotate replaces a key without increasing the count).
Quick start
Generate an API key above (approved travel agents only).
Top up your agency wallet in the Kawenter website or app.
Call the catalog → quote → create order → pay-wallet flow from your server.
Poll GET /orders/{id} until the order is complete.
Server-to-server only.
Keep your ka_live_… key on your backend. Do not embed it in mobile apps or browser JavaScript.
Send your key on every request using either header:
Headers
X-Api-Key: ka_live_…
# or
Authorization: Bearer ka_live_…
Base URL: https://kawenter.com/api/partner/v1
JSON only — Content-Type: application/json on POST
All amounts are USD
Errors
Errors use a consistent JSON shape with machine-readable details
so your integration knows exactly what broke (missing fields, scope, wallet shortfall, file limits, etc.).
Every response also sends X-Request-Id — include it when contacting support.
Error response
{
"ok": false,
"error_code": "INSUFFICIENT_WALLET",
"message": "Insufficient wallet balance.",
"status": 402,
"retry_after": null,
"request_id": "prt_a1b2c3d4e5f60718",
"method": "POST",
"path": "orders/123/pay-wallet",
"docs_url": "https://kawenter.com/docs/partner#errors",
"details": {
"balance_cents": 1200,
"required_cents": 5000,
"shortfall_cents": 3800,
"currency": "USD",
"hint": "Top up the agency wallet, then retry pay-wallet with the same Idempotency-Key.",
"action": "topup_wallet"
}
}
Key or agency quota — see Retry-After + limit_scope
502/503
UPSTREAM_ERROR / SERVICE_UNAVAILABLE
Temporary backend issue — retry with backoff
Idempotency
POST /orders/evisa requires an Idempotency-Key header
(or idempotency_key / client_reference in the body).
Retries with the same key return the original result — safe for network failures.
Recommended on pay-wallet as well.
Endpoints
Full workflow for integrating eVisa sales into your app or back office.
GET/api/partner/v1
Health check and agency identity for this API key.
Upload a passport or photo securely. Returns a file_id (no public URL).
Use that id on create-order. Max 15 MB; JPG/PNG/PDF/WEBP/HEIC/TIFF. Scope: files.
Server-priced quote for one or more applicants, plus full eVisa details
(requirements, important notes, processing time, add-ons).
Fee math is server-side only — never send addon_fee, insurance_fee, or totals.
Pass addon_id (from evisa.addons[].id) for catalog add-on prices × applicants.
Pass include_insurance: true to add catalog insurance once per order (not × applicants).
Create-order requires an add-on when the catalog lists any.
{
"ok": true,
"quote": {
"evisa_id": "73",
"title": "Afghanistan evisa",
"nationality": "EG",
"destination": "AF",
"applicant_count": 3,
"government_fee": 447,
"processing_fee_base": 138,
"processing_fee": 110.4,
"unit_addon_fee": 0,
"addon_fee": 0,
"selected_addons": [
{
"id": "addon_0",
"title": "Validity up to 30 Days",
"unit_price": 0,
"price": 0,
"currency": "USD"
}
],
"insurance_included": true,
"unit_insurance_fee": 9,
"insurance_fee": 9,
"insurance_refund_amount": 149,
"agency_discount_percent": 20,
"agency_discount_amount": 27.6,
"total": 566.4,
"currency": "USD",
"visa_type": "eVisa",
"validity": "30",
"duration_of_stay": "30",
"number_of_entry": "Single entry",
"processing_time": "5-7",
"processing_time_label": "5-7 working days",
"requirements": [
"Passport (6 months validity)",
"Copy of Recent Photo"
],
"important_notes": [
"Fees are non-refundable in case of Visa refusal",
"This visa is for tourism purposes only"
],
"evisa": {
"id": "73",
"title": "Afghanistan evisa",
"destination": "AF",
"requirements": ["Passport (6 months validity)", "Copy of Recent Photo"],
"important_notes": ["Fees are non-refundable in case of Visa refusal"],
"addons": [
{
"id": "addon_0",
"title": "Validity up to 30 Days",
"price": 0,
"currency": "USD"
}
],
"insurance": {
"available": true,
"fee": 9,
"unit_fee": 9,
"refund_amount": 149,
"currency": "USD"
}
}
}
}
POST/api/partner/v1/orders/evisa
Create an unpaid eVisa order with the same data as the website new-application page.
Fees (government, processing, add-ons, insurance) are recomputed server-side — never send fee amounts.
Check quote.required_fields after quoting (job / mother / travel dates vary by catalog).
Prefer passport_file_id / photo_file_id from POST /files.
Kawenter processes these orders like website applications (documents, tracking, status webhooks).
Header / field
Required
Notes
Idempotency-Key
Yes
Unique per create attempt (max 128 chars)
evisa_id, nationality
Yes
addon_id / addons
When catalog has add-ons
Use evisa.addons[].id. Price from catalog × applicants.
Send one applicants[] entry per traveler (up to 20). Fees are multiplied by traveler count; WooCommerce receives newline-joined passport_url, photo_url, work, and mother metas — same pattern as the website multi-traveler checkout.
Fetch a single order owned by your agency. Prefer webhooks over polling.
When more documents are needed, WooCommerce status is on-hold or not_completed_doc
(same as check-status “Documents uncompleted”): needs_documents is true,
extra_docs has the staff note, and check_status_url opens the public status page.
Download the approved visa PDF (or image) with your API key — no Kawenter login.
Returns binary with Content-Disposition: attachment.
404 DOCUMENT_NOT_READY until the visa is issued.
List recent partner orders. Default is a fast index (no WooCommerce round-trips).
Use ?expand=1 for full live order objects (fetched in parallel).
Prefer webhooks + GET /orders/{id} for status instead of polling expand lists.
Register an HTTPS callback so Kawenter notifies you when an order changes —
no polling required (important with the 1,000/day default). Scope: webhooks.
Signature header: X-Kawenter-Signature: t=<unix>,v1=<hmac_sha256(t + "." + body, signing_secret)>
plus X-Kawenter-Timestamp and X-Kawenter-Event-Id.
Reject events older than ~5 minutes and ignore duplicate event_id values (replay protection).
When has_document is true, call GET …/documents.
When needs_documents is true, show extra_docs or open check_status_url.
import os, requests
API = "https://kawenter.com/api/partner/v1"
KEY = os.environ["KAWENTER_API_KEY"]
def kawenter(path, method="GET", json=None, idempotency_key=None):
headers = {"X-Api-Key": KEY, "Accept": "application/json"}
if idempotency_key:
headers["Idempotency-Key"] = idempotency_key
r = requests.request(method, API + path, headers=headers, json=json, timeout=60)
data = r.json()
if r.status_code >= 400:
raise RuntimeError(data.get("message") or r.text)
return data
wallet = kawenter("/wallet")
quote = kawenter("/quotes/evisa", "POST", {
"evisa_id": "123",
"nationality": "IQ",
"applicant_count": 1,
})
Rate limits
Limits are per API key (not per agency and not per IP).
Every authenticated call counts against both windows.
Quotas are also returned on GET /api/partner/v1.
{
"ok": false,
"error_code": "RATE_LIMIT_EXCEEDED",
"message": "Partner API per-minute rate limit exceeded for this API key. Slow down and retry after Retry-After seconds.",
"status": 429,
"retry_after": 42,
"limit": 30,
"window": 60,
"remaining": 0,
"reset": 1730000042,
"limit_scope": "key",
"window_name": "minute",
"request_id": "prt_a1b2c3d4e5f60718",
"docs_url": "https://kawenter.com/docs/partner#rate-limits",
"details": {
"limit": 30,
"window": 60,
"window_name": "minute",
"remaining": 0,
"reset": 1730000042,
"limit_scope": "key",
"hint": "Per-key minute quota exhausted. Back off using Retry-After.",
"action": "retry_after"
}
}
Tips: reuse Idempotency-Key on retries, cache catalog results,
and use separate keys for production vs staging.
Need a higher limit? Contact Kawenter support.