Skip to content

AI Assistant PRO

Trip editor — AI sparkle affordance generates a description, SEO title, and meta description in one click

The AI Assistant adds in-place AI writing help across Yatra's editors — trip descriptions, SEO copy, replies to customer enquiries, even a daily dashboard summary. You bring your own key from OpenAI or Anthropic (both are well-known AI providers — like an electricity meter, you pay them directly for what you use), and Yatra simply wires that key into the editors. There's no Yatra markup on top, and your content goes straight to the provider you chose without passing through any Yatra server in between.

Which AI provider should a beginner pick?

Start with OpenAI and the gpt-4o-mini model. It's cheap (around $0.15 per million input tokens at the time of writing — typical operators spend $1–3 per 100 trip generations), fast, and works well for travel copy. Anthropic's claude-3-5-sonnet-latest produces more polished prose but costs ~5× more — switch to it once you know you'll use the assistant heavily. You can save both keys and switch per-prompt later.

What you'll need

ThingWhere to get it
Yatra Pro license — Growth or Scale tierYatra → License
AI Assistant module enabledYatra → Modules → AI Assistant
OpenAI API key (or Anthropic — pick one)platform.openai.com → API keys · console.anthropic.com → API keys
~$5–20 in provider creditOpenAI / Anthropic dashboard — most operators spend $1–3 per 100 generations

Per-token billing is between you and the provider. Yatra never proxies AI traffic and adds zero markup. Your API key talks directly to OpenAI or Anthropic.

What it unlocks

The module surfaces six AI affordances across the admin:

SurfaceWhat the AI does
Trip editor — title, short description, full description, itinerary, inclusions/exclusionsDrafts, rewrites, expands, or shortens any field. Honors your brand voice settings.
Trip SEO — meta title, meta description, focus keywordGenerates SEO-optimised copy in your brand voice from the trip's content.
Categories / destinations / activities — descriptionsOne-shot drafts from the taxonomy name.
Enquiry inboxDraft replyReads the enquiry thread + linked trip + customer history; drafts a courteous reply tailored to the lead's question.
Dashboard digestA daily AI-written summary of bookings, revenue, top trips, and what to do next.
Brand voice settingsA single configuration block all surfaces share so generated copy stays consistent.

Step 1 — Enable the module

  1. Open Yatra → Modules.
  2. Find AI Assistant → toggle on. (The toggle is disabled on Starter tier — you'll see an Upgrade to Growth or Scale CTA instead.)
  3. A new menu entry AI Assistant appears in the sidebar.

Step 2 — Connect your provider

Open Yatra → AI Assistant → Providers.

Providers tab — paste an OpenAI or Anthropic key, click Test, see the masked last-4 hint

FieldNotes
ProviderOpenAI (default) or Anthropic. You can configure both and pick per-prompt.
API keyPaste your provider key. Yatra encrypts it with strong industry-standard cryptography (XChaCha20-Poly1305 via the libsodium library) before saving — once stored, the browser only ever shows you the last 4 characters as a reminder. The key is never written to logs, never sent back to the React UI, and never leaves your server except when calling the provider directly.
TestSends a tiny ping to the provider's /models endpoint to verify the key works. Returns a clear error if billing isn't enabled or the key has been revoked.

Multi-provider failover

Save keys for both providers. Surfaces that fail with provider A (rate-limited, model deprecated, billing depleted) can fall back to provider B via the yatra_ai_providers filter. Recommended for Scale-tier clients with high-volume editorial use.

Step 3 — Set your brand voice

Open Yatra → AI Assistant → Brand Voice.

FieldWhat it shapes
Default providerWhich provider runs when a generation doesn't specify one.
Default modelSpecific model id (e.g. gpt-4o-mini, claude-3-5-sonnet-latest). Leave blank to use the provider's default.
ToneProfessional / Friendly / Adventurous / Luxury / Casual / Custom. Injected into every prompt so generated copy sounds like your brand.
Brand descriptionOne paragraph describing your company. Used as context in every generation.
Things to avoidBullet list of phrases / vocabulary to never use ("synergy", "world-class", competitor names, etc.). Drafts are post-filtered against this list.
Preferred reading levelGeneral / Grade 8 / Grade 12 / Native English speakers only. Drives the AI's sentence length and vocabulary.
Target audienceOptional one-line audience description (e.g. "American honeymooners 35–55, family budget").

Save once — every AI surface picks these up automatically.

Step 4 — Generate from the trip editor

In Yatra → Trips → Edit a trip, look for the small sparkle icon next to each long-text field.

Sparkle popover — Generate / Improve / Shorten / Expand / Rewrite in another tone

Click the sparkle to open the popover:

ActionWhat it does
GenerateDrafts new content from scratch using the trip's title + destination + duration as context. Useful for empty fields.
ImproveRewrites the current text for clarity, grammar, and brand voice — preserves your intent.
ShortenCuts to ~60% of current length without losing key facts.
ExpandGrows the text with more sensory detail, itinerary hints, and travel-story rhythm.
Rewrite in another toneSwitches the tone for one generation without changing your brand voice default.

Diff view lets you accept / reject / regenerate before the text overwrites your field.

Step 5 — Draft enquiry replies

Open an enquiry under Yatra → Enquiries → View an enquiry. Click Draft reply with AI.

The assistant reads:

  • The enquiry thread (every message back and forth)
  • The trip the lead is asking about (title, price, duration, key facts)
  • The customer's history if they've booked before
  • Your saved brand voice + tone

…and writes a reply that answers the lead's specific question + offers a clear next step. You can edit before sending — drafts are never sent automatically.

Step 6 — Dashboard daily digest

The Yatra dashboard's "Today" widget gets an AI-written paragraph each day summarising:

  • New bookings + revenue (vs. last week / last year)
  • Top-performing trips this week
  • Departures starting today or tomorrow
  • Pending tasks (unread enquiries, unpaid bookings, low-availability dates)

Generated once per day on first admin load — cached for 24h so you don't burn tokens on every page refresh.

Usage + cost monitoring

Open Yatra → AI Assistant → Usage.

ColumnNotes
DateDay bucket.
ProviderOpenAI / Anthropic.
GenerationsTotal successful generations.
Tokens in / outPrompt + completion tokens consumed.
Approx cost (USD)Computed from the model's published rate at the time of the call. Use it as a sanity check against your provider invoice.

The usage tab is also where you'd notice a runaway cost (a broken integration looping generations) before your provider bill lands.

REST API

For agencies embedding AI generation into custom workflows:

EndpointVerbPurpose
/yatra/v1/ai/metaGETModule eligibility + provider list + brand voice snapshot.
/yatra/v1/ai/generatePOSTGenerate text from a prompt + brand voice.
/yatra/v1/ai/improvePOSTImprove / shorten / expand existing text.
/yatra/v1/ai/brand-voiceGET / PUTRead or update brand voice settings.
/yatra/v1/ai/keys/{provider}POST / DELETESet or clear a provider API key.
/yatra/v1/ai/keys/{provider}/testPOSTTest connectivity to the provider.
/yatra/v1/ai/usageGETLast 30 days of usage rows.
/yatra/v1/ai/enquiry/{id}/draft-replyPOSTDraft an AI reply for an enquiry.
/yatra/v1/ai/dashboard/digestGETToday's AI digest (cached 24h).

All write endpoints require manage_options capability + a Growth/Scale-active license. See REST API reference for the full schema.

Hooks & filters

HookPurpose
yatra_ai_providers (filter)Add or replace AI providers. Return a map of { id: ProviderInstance }.
yatra_ai_prompt (filter)Last-chance edit of the prompt before it's sent to the provider — useful for compliance-mandated additions.
yatra_ai_result (filter)Post-process the generated text (e.g. profanity filter, regex cleanup).
yatra_ai_brand_voice (filter)Override the brand voice payload per-request — e.g. switch tone for a "luxury collection" subset of trips.
yatra_ai_should_log_usage (filter)Disable per-request usage logging if you have GDPR concerns about prompt content. Returns bool.
yatra_ai_generation_completed (action)Fires after each successful generation with the result + usage data.
yatra_ai_generation_failed (action)Fires when a generation throws — useful for alerting.
yatra_ai_pii_mode (filter)soft / strict / off — see PII masking.
yatra_ai_data_residency_region (filter)us / eu / global / empty — stamped on every audit row.
yatra_ai_region_endpoints (filter)Per-provider region → URL map. Used by the data-residency helper to pick the endpoint.
yatra_ai_disclosure_mode (filter)off / audit / prefix / suffix — see Transparency disclosure.
yatra_ai_disclosure_mode_for_task (filter)Per-task override of the disclosure mode.
yatra_ai_disclosure_text (filter)Override the default disclosure text.
yatra_ai_allow_call_for_context (filter)Return false to block an AI call before it leaves your server.
yatra_ai_retry_attempts (filter)Override the default 3 retry attempts per provider.
yatra_ai_fallback_provider (filter)Override which provider is the fallback when the primary fails.
yatra_ai_is_transient_error (filter)Widen / narrow the heuristic that decides which errors are retryable vs permanent.

Privacy

  • Prompts are sent directly to your configured provider — they don't transit any Yatra-owned infrastructure.
  • The Usage tab records token counts, not prompt content. Prompt + response content is never persisted server-side beyond the request lifecycle. The yatra_ai_should_log_usage filter lets you disable even the token counts.
  • Your API key is encrypted at rest with libsodium AEAD and decrypted just-in-time per request.

PII masking for context fields

Yatra builds context bundles for AI calls — for example, when you click "Draft reply" on an enquiry, it sends the customer name, email, phone, dietary requirements, medical conditions, and so on to the provider so the reply is informed. Operators with strict data-handling rules can change how those fields are sent.

Set the mode with the yatra_ai_pii_mode filter. Three modes:

ModeWhat it does
soft (default)Partial mask preserving structure — j***@example.com, +•••••1234. Free-text fields (dietary, medical, notes) pass through unchanged. Good for prompts where the AI benefits from seeing the shape of contact info.
strictReplace with a deterministic hash placeholder — [email:a1b2c3], [phone:d4e5f6]. Free-text fields are replaced with [REDACTED: <field>] markers — the AI knows the field exists without seeing the content. For operators on strict data-residency or HIPAA-style obligations.
offPass through unmodified. For operators on a DPA with the provider that legally permits PII transmission.
php
add_filter('yatra_ai_pii_mode', fn() => 'strict');

The mode applies uniformly to every AI surface, so an operator sets it once and every loader respects it.

Data residency

If your compliance posture requires AI traffic to stay in a specific region, set the residency once with the yatra_ai_data_residency_region filter:

php
add_filter('yatra_ai_data_residency_region', fn() => 'eu');

Recognized values: us, eu, global, or empty (no pinning — provider defaults). The audit log stamps the configured region on every call (success, failure, or blocked), so a compliance review can prove which region every prompt was sent to.

Note that the public OpenAI and Anthropic APIs are currently single-endpoint globally — there isn't a separate eu.api.openai.com for the chat-completions API. Real EU residency for OpenAI today means an Azure OpenAI tenancy; for Anthropic it means AWS Bedrock or GCP Vertex with the right region. If you're on one of those, point Yatra at it with the per-provider endpoint filter:

php
add_filter('yatra_ai_openai_endpoint', fn() => 'https://your-eu-tenant.openai.azure.com/...');
add_filter('yatra_ai_anthropic_endpoint', fn() => 'https://bedrock-runtime.eu-central-1.amazonaws.com/...');

The provider-specific endpoint filter wins over the region default — the region setting is what gets stamped in the audit log so the contract is auditable, the actual endpoint is whatever you configure.

Transparency disclosure

Some jurisdictions require that AI-generated text shown to customers is marked as such. The disclosure helper supports four modes via the yatra_ai_disclosure_mode filter:

ModeWhat it does
off (default)No change to the AI output.
auditStamps the audit log row with disclosure_required=true but doesn't alter the text. Use when the visible disclosure is rendered elsewhere (a UI badge, a wrapper component) and you just need proof that the policy was active on every call.
prefixPrepends the configurable disclosure text.
suffixAppends the configurable disclosure text.

The disclosure text itself defaults to "— Generated with the help of AI" and can be overridden:

php
add_filter('yatra_ai_disclosure_text', fn() => "\n\n(AI-generated. Edit before sending.)");

Per-task override via the yatra_ai_disclosure_mode_for_task filter — useful when you only want to disclose customer-facing outputs and leave internal admin summaries alone:

php
add_filter('yatra_ai_disclosure_mode_for_task', function ($mode, $task) {
    if (in_array($task, ['enquiry-reply', 'chat-draft'], true)) return 'suffix';
    return $mode;  // off for everything else
}, 10, 2);

The helper is idempotent — it won't double-tag text that already contains the disclosure line (some prompts ask the model to include it directly).

Per-call opt-out

If you need to block AI calls for specific contexts — a flagged medical-tour trip, a customer who asked not to have AI involved, an enquiry from an EU resident on a site without a DPA — use the yatra_ai_allow_call_for_context filter. Return false to block the call before it leaves your server:

php
add_filter('yatra_ai_allow_call_for_context', function ($allowed, $task, $context, $provider) {
    if (($context['trip_id'] ?? 0) === 42) return false;  // flagged trip
    return $allowed;
}, 10, 4);

Blocked calls throw a RuntimeException ("This AI call was blocked by a site policy filter.") and write a status=failure row to the audit log so operators can verify the rule fired.

Append-only audit log

Every AI call writes a row to wp_yatra_ai_audit_log covering: task, provider, model, prompt tokens, completion tokens, status (success / failure), prompt hash (SHA-256, not the raw prompt), context hash, error message, and a meta JSON column for region / disclosure mode / fallback markers.

There's no update or delete API on the audit log — it's append-only by contract. The retention cron deletes rows older than the configured cut-off (default not auto-pruned; set via the same yatra_ai_audit_persist_prompt and related filters described in the source).

Operators who need full-text prompt retention (legal hold, debugging) can extend the persistence:

php
add_filter('yatra_ai_audit_persist_prompt', fn() => true);

The default stores only the SHA-256 hash so the log proves a specific prompt was sent without persisting the contents.

Provider fallback + retry

Single-provider failures shouldn't break user-facing flows. The client wraps each call in two resilience layers:

  1. Retry the same provider with exponential-backoff-plus-jitter on transient errors (HTTP 429, 5xx, timeout, "unavailable", "overloaded"). Default 3 attempts. Permanent errors (401 bad-key, 400 validation) bail immediately — retrying won't help.
  2. Fall back to the alternate provider if all primary attempts fail AND the secondary provider has a configured key AND the failure looked transient. One attempt on the secondary using its default model.

Tune via filters:

php
add_filter('yatra_ai_retry_attempts', fn() => 5);          // raise from 3
add_filter('yatra_ai_fallback_provider', fn($id) => 'anthropic');  // pin fallback

The audit log records which provider actually served the response and whether a fallback was used (fallback_used: true in the meta column).

Streaming response support (experimental)

A new runStreaming() entry point on the AI client lets a frontend consumer subscribe to token-by-token output as the model produces it. The interface is opt-in:

  • Providers that implement StreamingProvider stream tokens via a callback.
  • Providers that don't (e.g. custom community providers added via yatra_ai_providers) gracefully degrade to a single "final" chunk after the regular complete() returns — consumer code stays identical either way.

No UI surface ships with the streaming hook yet — the chat-draft and itinerary-generation surfaces currently use the non-streaming run(). The hook is in place so a future iteration can land per-surface streaming without breaking the existing call path.

Troubleshooting

"AI Assistant" doesn't appear in the sidebar — verify (1) your license is Growth or Scale on the Yatra → License page, (2) the module is toggled on under Yatra → Modules. Starter-tier licenses see the upgrade card under Yatra → AI Assistant but no sparkle affordances.

"Test" button reports 401 Unauthorized — the key is wrong, revoked, or you copied a publishable key instead of a secret key. Regenerate in your provider dashboard.

"Test" passes but generations fail with insufficient_quota — your provider account needs billing enabled or a top-up. OpenAI and Anthropic both require pre-paid credit before any API calls succeed.

Generations are off-brand or generic — fill in the Brand description, Things to avoid, and Target audience fields under Brand Voice. The provider has no context about your business beyond what you tell it.

Generations are slowgpt-4o, gpt-4-turbo, and claude-3-opus are the slowest models. Switch to gpt-4o-mini or claude-3-5-haiku for a 5–10× speed-up at ~80% of the quality.

Token cost feels high — switch the default model to a smaller / cheaper variant under Brand Voice. The cost difference between flagship and mini models is typically 10–20×.

Where to read more