Tirth.comPartner APIs
For your industryHow it worksPartner APIsPartner ConsoleDevelopersWhy Tirth
Partner Sign InBecome a Tirth Partner
Partner API

Production reference · v1

Partner API documentation

One integration for ritual inventory, pilgrimage packages, prepaid credit payments, and fulfilment updates.

Download YAMLDownload JSONInteractive reference

Get started

Quick startAuthentication

Catalog & pricing

CatalogsPackage quotes

Checkout & fulfilment

CreditsBookings

Events

Webhooks

Reference

ErrorsAccess & pricingSupport

Quick start

Approved partners receive one production API key with explicit scopes. Fund the partner wallet, then create credit-paid bookings that confirm synchronously and emit signed lifecycle webhooks.

  1. 1Store your API key only on your server.
  2. 2Fetch rituals or packages with the appropriate read scope.
  3. 3Create a package quote and use it before it expires.
  4. 4Create a booking with payment_method set explicitly to CREDITS.
  5. 5Verify webhook signatures and reconcile with the relevant status endpoint.

Authentication

Send your production credential in the X-API-Key header. Never expose it in browser or mobile code. Keys are revocable and limited by scope and requests per minute.

curl 'https://api.tirth.com/v1/partner/packages?limit=20' \
  -H 'X-API-Key: tirth_pk_<your-key>'
const response = await fetch('https://api.tirth.com/v1/partner/packages?limit=20', {
  headers: { 'X-API-Key': process.env.TIRTH_API_KEY }
});
const packages = await response.json();
import requests

response = requests.get(
    "https://api.tirth.com/v1/partner/packages",
    params={"limit": 20},
    headers={"X-API-Key": TIRTH_API_KEY},
)
packages = response.json()

Responses include RateLimit-Limit and RateLimit-Remaining. A 429 response also includes Retry-After.

Ritual and package catalogs

GET/rituals

List active personal and upcoming Samuhik rituals.

Parameters

NameInTypeDescription
typequerystring (personal | samuhik)Filter to one ritual type.
searchquerystringFree-text search across title and location.
cityquerystringFilter by city.
statequerystringFilter by state.
destinationquerystringFilter by destination.
updated_sincequeryISO dateReturn only rituals updated at or after this timestamp.
limitqueryinteger (1–100)Page size. Defaults to 20.
offsetqueryintegerPagination offset.

Example response

{
  "count": 20,
  "total": 143,
  "has_next": true,
  "results": [
    {
      "id": "rit_9f2c",
      "type": "personal",
      "slug": "rudrabhishek",
      "title": "Rudrabhishek Puja",
      "overview": "...",
      "importance": "...",
      "location": { "name": "Kashi Vishwanath", "city": "Varanasi", "state": "Uttar Pradesh", "district": "Varanasi" },
      "images": ["https://..."],
      "benefits": ["..."],
      "process": ["..."],
      "faqs": [{ "question": "...", "answer": "..." }],
      "inclusions": ["..."],
      "add_ons": [{ "id": "addon_1", "title": "Extra Sankalp", "description": "...", "price": 501, "currency": "INR" }],
      "solutions": ["..."],
      "livestream_available": true,
      "updated_at": "2026-08-30T10:00:00.000Z",
      "pricing": { "currency": "INR", "retail_price": 2100, "max_people": 4 }
    }
  ]
}
  • ·Catalog responses support ETag/If-None-Match — send it during sync and treat a 304 as unchanged.
  • ·pricing shape differs by type: personal → {currency, retail_price, max_people}; samuhik → {currency, scheduled_for, plans[], pay_as_you_wish}.
GET/rituals/{type}/{slug}

Read plans, pricing, benefits, inclusions, add-ons, and livestream availability for one ritual.

Parameters

NameInTypeDescription
type*pathstring (personal | samuhik)Ritual type.
slug*pathstringRitual slug.

Example response

{
  "id": "rit_9f2c",
  "type": "personal",
  "slug": "rudrabhishek",
  "title": "Rudrabhishek Puja",
  "location": { "name": "Kashi Vishwanath", "city": "Varanasi", "state": "Uttar Pradesh", "district": "Varanasi" },
  "add_ons": [{ "id": "addon_1", "title": "Extra Sankalp", "price": 501, "currency": "INR" }],
  "livestream_available": true,
  "updated_at": "2026-08-30T10:00:00.000Z",
  "pricing": { "currency": "INR", "retail_price": 2100, "max_people": 4 }
}
  • ·Same object shape as a single item from GET /rituals.
GET/packages

List online bookable legacy and published pilgrimage packages with starting prices.

Parameters

NameInTypeDescription
searchquerystringFree-text search.
destinationquerystringFilter by destination.
package_typequerystringFilter by package type.
updated_sincequeryISO dateReturn only packages updated at or after this timestamp.
travel_start_datequerydate (YYYY-MM-DD)Filter to packages available for the full trip starting on this date.
limitqueryinteger (1–100)Page size. Defaults to 20.
offsetqueryintegerPagination offset.

Example response

{
  "count": 20,
  "total": 37,
  "has_next": true,
  "results": [
    {
      "id": "pkg_4a11",
      "slug": "char-dham-yatra",
      "title": "Char Dham Yatra",
      "subtitle": "The complete Himalayan circuit",
      "overview": "...",
      "duration": { "days": 11, "nights": 10 },
      "package_type": "group",
      "destinations": ["Yamunotri", "Gangotri", "Kedarnath", "Badrinath"],
      "images": { "cover": "https://...", "gallery": ["https://..."] },
      "pricing": { "currency": "INR", "starting_from": 24999, "unit": "per_person", "basis": "double_occupancy" },
      "package_kind": "PILGRIMAGE",
      "pricing_strategy": "FIXED_PER_PERSON",
      "availability": { "type": "ALL_TIME", "ranges": [] },
      "updated_at": "2026-08-30T10:00:00.000Z"
    }
  ]
}
  • ·Catalog responses support ETag/If-None-Match — send it during sync and treat a 304 as unchanged.
GET/packages/{slug}

Read the itinerary, pricing type, terms, legacy options, and pilgrimage departures.

Parameters

NameInTypeDescription
slug*pathstringPackage slug.

Example response

{
  "id": "pkg_4a11",
  "slug": "char-dham-yatra",
  "title": "Char Dham Yatra",
  "duration": { "days": 11, "nights": 10 },
  "pricing": { "currency": "INR", "starting_from": 24999, "unit": "per_person", "basis": "double_occupancy" },
  "pickup_point": "Haridwar Railway Station",
  "starting_place": "Haridwar",
  "ending_place": "Haridwar",
  "inclusions": ["..."],
  "exclusions": ["..."],
  "best_visit_time": "May – June, September – October",
  "terms_and_conditions": ["..."],
  "faqs": [{ "question": "...", "answer": "..." }],
  "itinerary": [{ "day_number": 1, "title": "Arrival in Haridwar", "description": "..." }],
  "selectable_options": [
    { "id": "opt_1", "type": "add_on", "title": "VIP Darshan", "description": "...", "day_number": 4, "currency": "INR", "price": 1500 }
  ]
}

Catalog responses support ETags. Send If-None-Match during synchronization and treat a 304 response as unchanged.

Package quotes

Quotes are calculated from the trusted catalog itinerary, traveller count, hotel room rules, transport capacity, selected options, markup, and GST. Client-supplied itineraries and prices are not accepted.

curl -X POST 'https://api.tirth.com/v1/partner/package-quotes' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: tirth_pk_<your-key>' \
  -d '{
    "package_slug": "char-dham-yatra",
    "travel_start_date": "2026-10-12",
    "participants_count": 1,
    "selected_option_ids": []
  }'
const quote = await fetch('https://api.tirth.com/v1/partner/package-quotes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.TIRTH_API_KEY
  },
  body: JSON.stringify({
    package_slug: 'char-dham-yatra',
    travel_start_date: '2026-10-12',
    participants_count: 1,
    selected_option_ids: []
  })
}).then(response => response.json());
import requests

response = requests.post(
    "https://api.tirth.com/v1/partner/package-quotes",
    headers={"X-API-Key": TIRTH_API_KEY},
    json={
        "package_slug": "char-dham-yatra",
        "travel_start_date": "2026-10-12",
        "participants_count": 1,
        "selected_option_ids": [],
    },
)
quote = response.json()
Quotes expire after 15 minutes, belong to the API key that created them, and can be consumed only once.
POST/package-quotes

Create a server-calculated, expiring quote for a package before booking it.

Body parameters

NameInTypeDescription
package_slug*bodystringThe package to quote.
travel_start_datebodydate (YYYY-MM-DD)Required for legacy and flexible-date packages; optional for scheduled departures.
participants_count*bodyinteger (1–50)Number of travellers.
selected_option_idsbodystring[]Legacy packages only: IDs from selectable_options.
pricing_selectionbodyobjectPilgrimages: adult/child/infant counts, group tier when needed, and departure ID for scheduled trips.

Example response

{
  "quote_id": "pq_8cf1a2",
  "package": { "slug": "char-dham-yatra", "title": "Char Dham Yatra" },
  "participants_count": 2,
  "pricing_selection": { "adults": 2, "child_with_bed": 0, "child_without_bed": 0, "infants": 0, "departure_id": "68a123456789abcdef012345" },
  "travel_dates": { "from": "2030-10-12", "to": "2030-10-22" },
  "amount": 4200,
  "currency": "INR",
  "price_breakdown": { "package_price": 4000, "discount_amount": 0, "gst_rate": 5, "gst_amount": 200, "total_amount": 4200 },
  "expires_at": "2030-10-01T12:15:00.000Z"
}
  • ·Quotes expire 15 minutes after creation.
  • ·The quoted price is fixed, but seats are checked again when booking.
  • ·Each quote is scoped to the API key that created it and can be consumed exactly once.
  • ·Returns 201 Created.

Prepaid credits

Credit-funded bookings confirm immediately without redirecting a customer to a payment page. One credit equals ₹1, credits do not expire, and a booking requires the full balance—split payments are not supported.

payment_method: CREDITS is required. Omission or any other value returns 422 CreditsRequired. If the balance is insufficient, the API returns 402 and does not consume credits or the package quote.
GET/credits

Read the current prepaid balance and lifetime totals.

Example response

{
  "balance_credits": 71550,
  "currency": "INR",
  "conversion": { "credits": 1, "inr": 1 },
  "lifetime_credited": 200000,
  "lifetime_debited": 128450
}
  • ·Requires credits:read. One credit equals ₹1 and credits do not expire.
GET/credits/transactions

List cursor-paginated wallet ledger entries.

Parameters

NameInTypeDescription
limitqueryinteger (1–100)Page size. Defaults to 25.
cursorquerystringnext_cursor from the previous response.
typequerycredit transaction typeOptional entry-type filter.

Example response

{
  "count": 1,
  "has_next": false,
  "next_cursor": null,
  "results": [{
    "transaction_id": "pct_8cf1a2",
    "type": "BOOKING_DEBIT",
    "amount_credits": -128450,
    "balance_after": 71550,
    "currency": "INR",
    "partner_booking_id": "pb_c91a"
  }]
}
  • ·Ledger entries are immutable; corrections appear as compensating entries.

Booking checkout and status

Package bookings use product_type: package. Existing ritual booking requests remain valid without a product discriminator.

curl -X POST 'https://api.tirth.com/v1/partner/bookings' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: tirth_pk_<your-key>' \
  -H 'Idempotency-Key: order_78492_attempt_1' \
  -d '{
    "product_type": "package",
    "payment_method": "CREDITS",
    "quote_id": "pq_8cf1...",
    "partner_reference": "order_78492",
    "customer": { "full_name": "Sample Customer", "phone": "+919999999999" },
    "participants": [{ "name": "Sample Customer", "age": 34 }]
  }'
const booking = await fetch('https://api.tirth.com/v1/partner/bookings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.TIRTH_API_KEY,
    'Idempotency-Key': 'order_78492_attempt_1'
  },
  body: JSON.stringify({
    product_type: 'package',
    payment_method: 'CREDITS',
    quote_id: quote.data.quote_id,
    partner_reference: 'order_78492',
    customer: { full_name: 'Sample Customer', phone: '+919999999999' },
    participants: [{ name: 'Sample Customer', age: 34 }]
  })
}).then(response => response.json());
import requests

response = requests.post(
    "https://api.tirth.com/v1/partner/bookings",
    headers={
        "X-API-Key": TIRTH_API_KEY,
        "Idempotency-Key": "order_78492_attempt_1",
    },
    json={
        "product_type": "package",
        "payment_method": "CREDITS",
        "quote_id": quote["quote_id"],
        "partner_reference": "order_78492",
        "customer": {"full_name": "Sample Customer", "phone": "+919999999999"},
        "participants": [{"name": "Sample Customer", "age": 34}],
    },
)
booking = response.json()
POST/bookings

Create a credit-paid booking for a ritual or a quoted package.

Headers

NameDescription
Idempotency-Key*One stable key per logical checkout attempt (pattern ^[A-Za-z0-9_-]{1,255}$).

Body parameters

NameInTypeDescription
payment_method*body"CREDITS"Required. Partner API bookings confirm synchronously from the prepaid balance and do not return a payment URL.
product_typebody"package"Set to "package" for package bookings. Omit for ritual bookings.
quote_id*bodystringRequired for package bookings — from POST /package-quotes.
ritual_typebodystring (personal | samuhik)Required for ritual bookings.
ritual_slugbodystringRequired for ritual bookings.
selected_datebodydateRequired for ritual bookings.
selected_plan_idbodystringSamuhik plan selection.
selected_add_on_idsbodystring[]Ritual add-ons.
partner_reference*bodystringYour own order/reference ID, echoed back in webhooks and status lookups.
customer*bodyobject{ full_name, phone, address, email? }
puja_modebodystringRitual bookings only.
prasad_deliverybodyboolean | objectRitual bookings only.
participants*bodyarrayTraveller details. Pilgrimages require name, age, and traveller_type matching the quote.

Example response

{
  "partner_booking_id": "pb_c91a",
  "tirth_booking_id": "bk_71029",
  "amount": 128450,
  "currency": "INR",
  "payment_method": "CREDITS",
  "payment_status": "paid",
  "credits_debited": 128450,
  "credit_balance": 71550,
  "credit_transaction_id": "pct_8cf1a2",
  "payment_url": null
}
  • ·Retrying the same completed request with the same Idempotency-Key returns the original booking (200).
  • ·Reusing an Idempotency-Key with a different payload returns 409.
  • ·Omitting CREDITS or sending another payment method returns 422 CreditsRequired.
  • ·The full credit balance is required; insufficient balance returns 402 without consuming the booking or package quote.

Idempotency

Use one stable Idempotency-Key for every logical checkout attempt. Retrying the same completed request returns the original booking; reusing it with different data returns 409.

Status values

reservedpayment_pendingconfirmedcancellation_requestedcancelledrefund_pendingrefundedfailedexpired
GET/bookings/{partner_booking_id}

Reconcile payment and product-specific fulfilment status.

Parameters

NameInTypeDescription
partner_booking_id*pathstringID returned from POST /bookings.

Example response

{
  "partner_booking_id": "pb_c91a",
  "partner_reference": "order_78492",
  "product": { "type": "package", "slug": "char-dham-yatra" },
  "package": { "title": "Char Dham Yatra" },
  "amount": 128450,
  "currency": "INR",
  "status": "confirmed",
  "tirth_booking_id": "bk_71029",
  "fulfilment": {
    "status": "guide_assigned",
    "guide": { "name": "...", "phone": "..." },
    "livestream_url": null,
    "recording_url": null,
    "invoice_url": "https://...",
    "completed": false
  },
  "created_at": "2026-09-04T12:00:00.000Z",
  "updated_at": "2026-09-04T12:05:00.000Z"
}
  • ·status is one of: reserved, payment_pending, confirmed, cancellation_requested, cancelled, refund_pending, refunded, failed, expired.
POST/bookings/{partner_booking_id}/cancellations

Raise one operations-reviewed cancellation request for a paid ritual or package booking.

Parameters

NameInTypeDescription
partner_booking_id*pathstringPartner booking ID returned from POST /bookings.
reason*bodystring (3–1000 chars)Why the customer is requesting cancellation.
partner_cancellation_referencebodystringYour optional reconciliation reference.

Example response

{
  "partner_booking_id": "pb_c91a...",
  "status": "cancellation_requested",
  "cancellation": {
    "status": "requested",
    "reason": "Customer travel plans changed",
    "partner_cancellation_reference": "cancel_78492",
    "requested_at": "2026-09-27T12:00:00.000Z"
  },
  "refund": {
    "status": "not_requested",
    "reference": null,
    "amount": null,
    "currency": "INR",
    "completed_at": null
  }
}
  • ·The first request returns 202; repeats return the original request and current state with 200.
  • ·This requests review only. It does not guarantee cancellation or automatically issue a refund.
  • ·Requires the bookings:cancel scope.

Signed webhooks

Webhook bodies are signed with HMAC-SHA256 using your one-time-issued webhook secret. Compute the signature over the exact raw body and compare it safely with X-Tirth-Signature. Deduplicate using X-Tirth-Event-Id.

booking.confirmed
booking.payment_failed
booking.cancellation_requested
booking.cancelled
booking.refund_pending
booking.refunded
booking.pandit_assigned
booking.guide_assigned
booking.livestream_available
booking.completed

Return any 2xx status quickly. Failed deliveries are retried with bounded exponential backoff.

Errors

Errors use the same JSON envelope across the API. Handle 401 by checking the key, 403 by checking scopes, 409 as an idempotency or consumed-quote conflict, 422 as invalid/unavailable input, 429 with exponential backoff, and 503 as a temporary upstream outage.

{
  "status": "error",
  "message": "Package is unavailable for the full trip",
  "error": "InvalidRequestInput"
}

Access & pricing

Partner access is free during the partnership review, with custom terms agreed as you scale. There is no public self-service signup — every production integration is reviewed before we issue credentials.

How limits work

Each partner gets a rate limit set for their integration based on expected volume — not a fixed public tier. It defaults to 120 requests/minute and is negotiated up as you grow. See Authentication for the RateLimit-* response headers.

See the partner FAQ for how approval and onboarding work, or apply below.

Apply for access

Versioning and support

All supported endpoints are under /v1/partner. Additive fields may appear without a version change; breaking changes require a new version and migration window.

Need production access or integration help?

Apply with your use case or contact the partnerships team at hi@tirth.com.

Apply for access
curl 'https://api.tirth.com/v1/partner/packages?limit=20' \
  -H 'X-API-Key: tirth_pk_<your-key>'
const response = await fetch('https://api.tirth.com/v1/partner/packages?limit=20', {
  headers: { 'X-API-Key': process.env.TIRTH_API_KEY }
});
const packages = await response.json();
import requests

response = requests.get(
    "https://api.tirth.com/v1/partner/packages",
    params={"limit": 20},
    headers={"X-API-Key": TIRTH_API_KEY},
)
packages = response.json()
Tirth.com

Pilgrimage infrastructure for trusted customer platforms.

Partner APIsDevelopersBecome a PartnerPartner Sign In

Ritual API · Packages API · Assisted integration