Human-in-the-loop for travel and expense agents

Most trips are in policy. Exceptions need a person.

Your agent books what fits policy on its own and sends each exception to the budget owner's phone.

How do I let an AI travel agent auto approve inside policy and ask a human for exceptions?

Pushary lets an AI travel agent auto approve trips inside policy and ask a human about the exceptions. Your code books the in-policy fare on its own. For an out-of-policy fare, a cabin upgrade or an over-limit claim, the agent asks first. The budget owner gets a notification on their phone. They tap Approve or Deny. You can also ask them to type an instruction instead, like "find a cheaper fare". If nobody answers before the fare expires, the booking does not run and no card is charged.

What needs a yes

The calls that should not be automatic

bookFlight(itinerary, fareClass)
A fare above the policy ceiling is the classic exception, and the price may only hold for a few minutes. An approval email sitting in an inbox costs real money here. The fare is gone before anyone reads it.
upgradeCabin(bookingRef, cabin)
Cabin rules often turn on facts the agent does not have: how long the flight is, who is flying, whether a client is paying. The rule is often unwritten too. A person settles it in one tap.
approveExpense(reportId, amount)
Paying back an over-limit claim is money leaving the company, with no clean undo. It is also the line an auditor asks about later. The record of who approved it matters as much as the answer.
rebookDisruption(bookingRef, newItinerary)
A cancelled flight at 6am is when the agent helps most and is watched least. Rebooking into a higher fare to get someone home is usually right. It should still be a person's call rather than a default.
issueTravelAdvance(employeeId, amount)
An advance moves cash before any receipt exists, so the usual expense checks after the trip do not apply. The approval is the only check you have.

The code

One call inside the step you already have

Wrap the action, not the whole agent. Everything the agent does that is safe keeps running without asking anyone.

bash
npm i @pushary/server
one call
import { createPusharyServer } from "@pushary/server"

const px = createPusharyServer({ apiKey: process.env.PUSHARY_API_KEY! })

async function book(trip: TripRequest, fare: Fare) {
  // In policy and under the ceiling: the agent just books it.
  if (fare.total <= POLICY.ceilingFor(trip.route) && fare.cabin === "economy") {
    return bookFlight(trip.itinerary, fare.cabin)
  }

  const decision = await px.decisions.ask({
    externalId: trip.budgetOwnerId,
    type: "confirm",
    question: `Approve ${fare.currency}${fare.total} for ${trip.traveller}? Policy ceiling is ${POLICY.ceilingFor(trip.route)}.`,
    timeoutMs: 9 * 60 * 1000,
  })

  if (!decision.approved) return holdForCheaperFare(trip)
  return bookFlight(trip.itinerary, fare.cabin)
}

Who answers

The approver is not at a desk

The approver is the person whose budget the trip lands on. That is usually a line manager or a cost center owner, not travel operations. Travel teams run the policy, but they do not own the money. Send every exception to a shared travel inbox and it reaches people who cannot say yes. Pushary sends it to the budget owner's phone, and the answer comes back under their name.

We already have an approval step in our booking tool. Why add another one?

Most booking tools do have an approval step. It is usually an email with a link to a portal the approver last opened a month ago. That works for a trip planned three weeks out. It fails for a fare that expires in nine minutes, or a cancelled flight at six in the morning. The trouble is where the approval lands: on a channel nobody is watching when the decision matters. Pushary does not replace your travel policy or your booking tool. It puts the exception on the phone already in the approver's hand, with a deadline. If the deadline passes, the booking does not run. Every answer is saved with who decided and when, and you can export it. If your current step already gets answered inside the fare window, you do not need this.

Worth reading: IRS Publication 463 on travel expense substantiation, GSA per diem rates, the ceiling most US travel policies inherit.

$99/ month, Partner plan

Add it to your product. Your users never make a Pushary account or pay us.

What the Partner plan covers

FAQ

Questions, answered

Can an AI travel agent auto approve inside policy and only ask about exceptions?
Yes, and that is the split worth building. Compare the fare to your own ceiling in code and book it when it fits, so the common case needs nobody. Ask a person only about the exception. Put the policy limit in the question, so the approver sees which rule is broken and by how much. When exceptions are rare, almost every trip still books untouched.
What happens if the approver does not answer before the fare expires?
The booking does not run. Set the timeout shorter than the fare hold. When it runs out, the answer counts as no and the agent moves to your next step: hold for a cheaper fare, search again, or ask a second approver. The request is still saved, so an expired exception shows up later instead of vanishing. That matters here, because the alternative is a company card charge nobody approved.
Who should approve an out-of-policy trip, travel operations or the manager?
The budget owner, which usually means the manager. Travel operations run the policy, but the spend lands on a cost center someone else answers for. A yes from the travel desk is paperwork rather than a spending decision. Send the question to the manager by your own user id, and the answer comes back under their name. That is also what an auditor looks for later.
Does this work for expense reports as well as bookings?
Yes, and the shape is the same. Auto approve claims under your limit that have a matching receipt. Ask a person about claims over the limit, claims with no receipt, and the categories your policy calls out. The difference is timing. A booking is urgent and an expense report is not, so an expense approval can wait for hours. Save the report and pick it back up when the answer reaches your server.

Put a person on the step that needs one.

Connect a user once, ask with one call, and run the action only on a yes. Every answer is saved.

Start free