Perk
Plan trips and manage spend
- Category
- Travel & Hospitality
- Primary Subcategory
- Corporate Spend, Expense & Procurement
Integration details
Description
Perk brings your business travel and spend into ChatGPT. Plan and review trips, check bookings, track spend, and pull travel and expense reports - all without leaving the conversation. Built for Perk customers who want fast answers from their own travel and spend data.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Corporate Spend, Expense & Procurement
- Secondary Subcategories
- None listed
- Brand
- Perk
- Access
- Account required
- First tracked
- 2026-08-04
- Tool count
- 53
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Perk
Get updates when Perk’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Corporate Spend, Expense & Procurement
View Category53 tools agents can invoke
Approve a single expense that is awaiting the authenticated user's approval. Approving advances the expense to the next approver/stage in its approval chain. IMPORTANT — this performs a consequential state change. Before calling this tool you MUST: 1. Identify the specific expense (by id) and its submitter, typically via `query_expenses` with role='approver' (submitter display name from userFirstName + userLastName, or userId). 2. Explicitly confirm with the user that they want to approve THAT expense (state the submitter, amount, and merchant) before proceeding. Do not approve speculatively or in bulk. Authorization is enforced server-side: if the caller is not a current approver of the expense, or the expense is no longer pending, the approval fails and a clear message is returned. On success the tool returns a simple confirmation ({expenseId, approved: true, expenseUrl}); `expenseUrl` opens the expense in Perk's approver view. The updated expense is not re-read (an approved expense typically leaves the approver view).
expenses_approve_expense
Generate a Perk deep-link to the Configure page for a chosen car offer, with that offer preselected, so the user can complete the booking. Pass the group_id of the chosen CarOffer, and pass the offer_id of EVERY fare under that chosen offer in offer_ids - passing only one id degrades the page to a single rate option instead of the full rate and payment matrix. pick_up_location, drop_off_location, travelers_ids and selected_main_driver_id must be the exact same values passed to start_car_search for this search - the Configure page needs them to render and to let the user edit the search, and omitting or altering them breaks the page. pick_up_datetime, drop_off_datetime, full_credit_voucher_activated and emissions_co2_kg must come from the chosen CarOffer. Present the returned URL as a markdown hyperlink using the label field as the link text, e.g. [Complete your booking](url). Never display the raw URL.
cars_build_car_deeplink
Generate a Perk checkout deep-link for a selected train itinerary. Call this only after the user has confirmed their offer selection. For one-way trips: pass only the outbound parameters, using the outbound offer's own bundled_offer_id. For round-trips: pass both outbound and inbound parameters, using the inbound offer's bundled_offer_id (it represents the combined itinerary). Present the returned URL as a markdown hyperlink using the label field as the link text, e.g. [Complete your booking](url). Never display the raw URL.
trains_build_train_deeplink
Check visa and entry requirements for a flight itinerary. REQUIRED: destinations - the same leg objects passed to start_flight_search. Passport nationality is inferred from the origin airport. For round-trips, only check the outbound leg — the return goes back to the traveler's starting country where we can assume they are allowed to enter. For multi-city trips, call this tool once per origin→destination leg and present the combined results. Present results using the display_hint in the tool response. One sentence only — no headers, no disclaimers, no passport assumptions.
flights_check_visa_requirements
Create a new Perk Event. name, location, start_date and end_date are required; the event is created as a private, published event by default. Use visibility='account' to make it visible to the whole account, or is_draft=True to create it as a draft. location is a free-text description of where the event takes place (e.g. venue name, address, or neighborhood); the backend resolves it to a Google Maps place. category defaults to 'team or company event' and, when provided, must be one of: 'group trip', 'team or company event', 'external event', 'other'.
events_create_event
Create a new draft expense with no receipt (manual / out-of-pocket), using the same expense-service create contract as the Spend UI. IMPORTANT — before calling this tool you MUST: 1. Confirm merchant, amount, currency, and expense date with the user. 2. Prefer user-friendly progress language (e.g. “Creating a €12 lunch expense”). This tool is NOT idempotent: calling it again creates another expense. Do not retry on success. If the user has a receipt image/PDF, use `receipt_upload` (or `receipt_upload_fallback` when the client cannot PUT to a signed URL) instead of this tool. Required: merchant, total_amount, currency, expense_date. Optional: category_id, cost_center_items, comment, payment_method (defaults to paidPrivately), company_credit_card_id, tags, legal_entity_id. When legal_entity_id is omitted, the caller's home legal entity is used automatically. For selectable ids (category, cost centers, tags, cards), resolve options after create via `get_expense_form_options` / `update_expense`, or from known catalogs when the user already chose them. Successful responses include `expenseUrl`, which opens the expense in Perk. The expense starts as a draft — edit with `update_expense`, attach a receipt later via receipt upload tools, or submit with `submit_expense`.
expenses_create_expense
Start generating an org-wide spend report exported from Yokoy. Intended for admins and financial controllers — data scope covers the entire organisation or can be filtered by legal entity or user set. This is NOT a substitute for `expenses_query_expenses`, which is scoped to the individual traveller's own expenses. Export time varies: small date ranges are typically ready in under a minute, larger datasets can take several minutes to a few hours. Once started, poll `reporting_get_spend_report` with the returned task_id — call it every minute until `state` is `complete` or `error`. When `state` is `complete`, the response includes a `url` field with the download link.
reporting_create_spend_report
Start generating a travel report for your Perk account. Specify a date range and optional filters, then use the returned task_id to retrieve results. Use `reporting_get_travel_report_data` for structured JSON exploration, or `reporting_get_travel_report` for a full CSV/XLSX file download. Looker async results expire ~5 minutes after creation — fetch data promptly with `reporting_get_travel_report_data` (ideally within 4 minutes). Reports can only cover dates up to yesterday — today's data is not available. Date filter guidance: Use `booking_date` for procurement or expense transaction analysis — this is the date the booking was confirmed and a sale transaction was created. Use `start_date` to analyse trips by when travel actually happened. Use `invoice_issue_date` for finance reconciliation against invoices. Cost field guidance: Use `cost_per_traveler_preferred_currency` for spend totals — it converts all bookings to your company's reporting currency so cross-currency comparisons are accurate. Use `cost_per_traveler` if you need the original booking currency. Avoid the `estimated_*` fields for actuals reporting — they reflect pre-booking estimates, not final charges.
reporting_create_travel_report
Get the itinerary confirmation PDF download URL for a Perk trip. Returns a URL to the full booking confirmation including all services (flights, hotels, cars, trains). The trip must have at least one traveler and one booking. Present the returned URL as a markdown hyperlink using the label field as the link text, e.g. [Download PDF](url). Never display the raw URL.
trips_get_trip_itinerary_pdf
Fetch the upgrade options found for an offer, using the transaction_id and offer_id returned by start_flight_upsell. When status is 'loading' the upsell is still running: show any options already there, say more are still arriving, then wait retry_after_seconds and call this tool ONCE with the same parameters (do NOT call it multiple times in the same turn). Keep polling until status is 'complete'. The options returned are always bookable. partial_results being true means a provider failed, so cabins or fares it would have offered are missing - mention that more may be available, but present what came back normally. The results include the cabin the original offer was already on, so an option here may be the same flight the user started from - compare prices before describing one as an upgrade. total_results is how many options this response holds; when fewer offers than that come back the list was capped, so say so ('showing 20 of 24 options') rather than presenting it as everything. IMPORTANT: as in get_flight_leg_options, each price is the total for the complete itinerary (all legs), not the extra cost of upgrading - do NOT present it as a top-up, and do NOT add it to the original offer's price. If the user asks what the upgrade costs, subtract the original offer's price from the option's price and say so explicitly. Each option carries travel_class (e.g. 'economy', 'economy-comfort', 'business') and fare_names (e.g. ['MAIN CABIN'], one per segment). Use them to say what each option actually is: two options can share a travel_class and differ only by fare, which is what makes one refundable or changeable. Never infer the cabin from the price. When presenting options, ALWAYS display a table with a reference number (1, 2, 3, ...), Airline, Cabin, Fare, Departure, Arrival, Duration, Stops (show 'Direct' for 0 stops, otherwise 'N stop(s)'), Price, and In Policy - unless the user asks for a different format. To book one, pass its offer_id to build_flight_deeplink.
flights_get_flight_upsell_options
Fetch available flight offers for the current leg of a search. Called with the transaction_id returned from start_flight_search. When status is 'loading' the search is still running. Early on, offers is empty - tell the user the search is in progress, then wait retry_after_seconds and call this tool ONCE with the same parameters (do NOT call it multiple times in the same turn). Once the search has run for a few seconds, a 'loading' response also carries the offers found so far: show them, say more are still arriving, and keep polling. Stop early and use what you have only if the user asks to - otherwise keep polling until status is 'complete', which means no more offers are coming. The offers returned are always bookable. When partial_results is true some providers did not respond, so the list may be missing options - mention that more may be available if the user does not see what they want, but present the offers normally. For round-trips, call this tool sequentially - NOT in parallel: first with an empty leg_fare_selections to get outbound options, then only after the user has chosen an outbound flight, call again with the chosen outbound offer in leg_fare_selections to get return options. IMPORTANT: The price on each offer is the total trip cost for the complete itinerary (all legs). For round-trips, the price already covers both outbound and return - do NOT add the outbound and return prices together; either price alone is the full roundtrip total. Use the sorting parameter to let the user compare cheapest ('price'), fastest ('travel_time'), best-value ('best') or lowest-carbon ('emissions') options without starting a new search. Sort by departure or arrival time with 'departure_first_leg'/'arrival_first_leg'; these run earliest-first, so add sort_order='desc' when the user wants the latest instead. When the user narrows their request, re-call this tool with the SAME transaction_id and the matching filter: max_stops (0 for direct only), airlines, depart_after/depart_before, arrive_after/arrive_before, max_duration_minutes, max_layover_minutes, in_policy_only. Filtering re-queries the search that is already running; never start a new search to apply a filter. Filters combine, so keep the ones still wanted when adding another. The response reports total_results against total_results_without_filters: use it to tell the user how much a filter narrowed things. When total_results is 0 but total_results_without_filters is not, the search did find flights and the filters excluded them all - say which filter is too narrow and offer to relax it. Never report this as 'no flights available'. The response includes current_page and total_pages - re-call with page=2, 3, etc. when the user asks for more options, keeping all other parameters the same. Each offer is a different flight, shown at its cheapest fare, and carries travel_class and fare_names. The same flight is usually also sold as dearer fares in higher cabins - those are NOT listed here; use start_flight_upsell on an offer to see them. When presenting offers to the user, ALWAYS display a table that includes every offer with these columns (unless the user explicitly asks for a different format or level of detail): a reference number (1, 2, 3, etc. so the user can refer to a flight by number), Airline, Departure, Arrival, Duration, Stops (show 'Direct' for 0 stops, otherwise 'N stop(s)' - the connecting airports are not returned, so never name one), Price, and In Policy. IMPORTANT: For round-trip searches the price column MUST be labelled 'Round-trip total' (not 'Price'). For one-way searches label it 'Price'. Never omit these columns even when multiple offers share the same value - show the value in each row.
flights_get_flight_leg_options
Get a single invoice by its serial number. Returns full invoice details including billing information, tax summary, amounts, line items preview, and a PDF download URL.
invoices_get_invoice
Fetch available car rental offers for a search. Call this after start_car_search with the returned transaction_id. If the response status is 'PENDING', you MUST send a visible message to the user (not just internal reasoning) saying the car search is in progress and results will be ready shortly, then wait retry_after_seconds seconds and call this tool ONCE with the same parameters - do NOT call it multiple times in the same turn. Keep repeating until status is 'COMPLETE' - NEVER stop polling to ask the user whether to continue waiting, and NEVER give up early. 'COMPLETE' is the only status you will ever see besides 'PENDING', and it is returned as soon as any offers are available, even if some suppliers failed. A search that fails outright raises a tool error - report that error to the user rather than retrying it silently. If a search does not finish in time, you will instead see 'COMPLETE' with whatever offers were found so far (possibly none) - present those normally rather than treating it as an error. sort_by and the filter parameters (supplier, insurance_level, car_type, car_size, car_seats, car_transmission, car_fuel_type, payment_mode) are optional refinements for a follow-up, more specific poll - call again with them once the user narrows their preferences. filter_options on a COMPLETE response lists the values usable for each of these filters. When presenting offers to the user, ALWAYS display a table that includes every offer with these columns (unless the user explicitly asks for a different format or level of detail): a reference number (1, 2, 3, etc. so the user can refer to an offer by number), Supplier, Car, Type, Seats, Transmission, Pick-up, Drop-off, Mileage, Payment, Price, In Policy. Never omit these columns even when multiple offers share the same value - show the value in each row.
cars_get_car_search_results
Get the profile and identity of the currently authenticated user. Returns name, email, roles, account, cost centre, and other identity details.
identity_get_current_user
Load selectable options for editing an expense, using the same expense-service form-options APIs as the Spend UI. ALWAYS call this before proposing or applying updates that need ids (category, cost center, tags, payment method / company card, etc.). By default this fetches only the common edit catalogs: categories, costObjects (cost centers), tags, and paymentMethods. Pass `fields` to request additional or different catalogs (e.g. currencies, countries, customFields, trips). Pass `include_cards=true` when the user may select a company credit card — cards are not loaded by default. IMPORTANT — present options to the user in user-friendly language (labels and plain names), for example: “Available cost centers: Engineering, Marketing” or “Your cards: Visa ••••1234”. Do NOT dump raw JSON or lead with technical ids/enums. Keep ids only for the subsequent `update_expense` call after the user chooses.
expenses_get_expense_form_options
Fetch the rooms and rates for one hotel. Call after start_hotel_rates with the same transaction_id and hotel_id. Status 'PENDING' means the rooms are still being fetched: tell the user, wait retry_after_seconds and call again with the same parameters until status is 'COMPLETE'. more_rates counts rates left out: say so, and pass that room_id in show_all_rates_for on request. When total_pages is above page, offer the next page. Filter with the parameters, not by leaving rooms out. - ALWAYS open with the hotel name and hotel_address, e.g. 'AC Hotel Madrid Feria by Marriott - Via de los Poblados 3, 28033 Madrid, ES'. - ALWAYS display one table per room option, with its name as the heading, e.g. '2 Double Room; 1 Standard Room'. - ALWAYS show every rate's title, price_total, free_cancellation_until, is_in_policy and tags, each value exactly as returned. - ALWAYS use this table template, with a numbered row per rate: | # | Rate | Total | Free cancellation | In policy | Tags | | 1 | Breakfast included | EUR 502.92 | - | true | | - NEVER drop a column or move it into a footnote, however many rates leave it empty. - ALWAYS ask which rate they want and, in the same message, whether they have anything to pass on to the hotel, e.g. 'I arrive after midnight'. - NEVER pass anything internal on to the user, as a value or in your own words, e.g. room_id, rate_id, provider_id or which provider sells a rate.
hotels_get_hotel_rates
Fetch the hotels matching a search. Call after start_hotel_search with its transaction_id. Status 'PENDING' means the search is still running: tell the user, wait retry_after_seconds and call again with the same parameters until status is 'COMPLETE'. When total_pages is above page, offer the next page. Filter with the parameters, not by leaving hotels out; if the user asks for a filter there is no parameter for, say so. - ALWAYS open with search_location exactly as returned, e.g. 'Madrid, Community of Madrid, Spain'. - ALWAYS give every hotel its own numbered row, with these columns: name, price_total, stars, rating, proximities and policy_result.is_in_policy, e.g. '1 | AC Hotel Madrid Feria by Marriott | EUR 554.40 | 4 | 8.2 | 12 min by car | in policy'. - NEVER show hotel_id.
hotels_get_hotels
Fetch available inbound (return) train offers for a search. Call this after start_train_search_inbound with the returned transaction_id. If the response status is 'PENDING', you MUST send a visible message to the user (not just internal reasoning) saying the train search is in progress and results will be ready shortly, then wait retry_after_seconds seconds and call this tool ONCE with the same parameters - do NOT call it multiple times in the same turn. Keep repeating until status is 'COMPLETE' - NEVER stop polling to ask the user whether to continue waiting, and NEVER give up early. 'COMPLETE' is the only status you will ever see besides 'PENDING', and it is returned as soon as any offers are available, even if some providers failed. A search that fails outright raises a tool error - report that error to the user rather than retrying it silently. If a search does not finish in time, you will instead see 'COMPLETE' with whatever offers were found so far (possibly none) - present those normally rather than treating it as an error. These are the trains departing soonest after the requested return_departure_time. When the user wants different return times, start the whole search over with start_train_search using a later return_departure_time rather than re-calling this tool - there is no second page. When presenting offers to the user, ALWAYS display a table with the same columns used for outbound offers (reference number, Vendor, Origin, Destination, Departure, Arrival, Duration, Stops, Price, Fare, In Policy). IMPORTANT: unlike flights, the inbound price is NOT the round-trip total - the final cost is the outbound offer's price plus the inbound offer's price.
trains_get_train_search_inbound_results
Fetch available outbound train offers for a search. Call this after start_train_search with the returned transaction_id. If the response status is 'PENDING', you MUST send a visible message to the user (not just internal reasoning) saying the train search is in progress and results will be ready shortly, then wait retry_after_seconds seconds and call this tool ONCE with the same parameters - do NOT call it multiple times in the same turn. Keep repeating until status is 'COMPLETE' - NEVER stop polling to ask the user whether to continue waiting, and NEVER give up early. 'COMPLETE' is the only status you will ever see besides 'PENDING', and it is returned as soon as any offers are available, even if some providers failed. A search that fails outright raises a tool error - report that error to the user rather than retrying it silently. If a search does not finish in time, you will instead see 'COMPLETE' with whatever offers were found so far (possibly none) - present those normally rather than treating it as an error. These are the trains departing soonest after the requested departure_time. When the user wants different departure times, call start_train_search again with a later departure_time rather than re-calling this tool - there is no second page. When presenting offers to the user, ALWAYS display a table that includes every offer with these columns (unless the user explicitly asks for a different format or level of detail): a reference number (1, 2, 3, etc. so the user can refer to an offer by number), Vendor, Origin, Destination, Departure, Arrival, Duration, Stops (show 'Direct' for 0 stops, otherwise 'N change(s)'), Price, Fare (fare.name, or '-' if unknown), and In Policy. Summarize fare.description in your own words when present (e.g. refund/exchange terms) rather than showing it verbatim, and mention notable fare.amenities (e.g. a meal-inclusive fare) since they distinguish otherwise-similar fares. Never omit these columns even when multiple offers share the same value - show the value in each row.
trains_get_train_search_results
Check the status of a spend report started with `reporting_create_spend_report`. Poll every minute until state is 'complete' or 'error'. When state is 'complete', url contains a signed download link for the report file. Stop polling if state is 'error'.
reporting_get_spend_report
Get the authenticated user's travel policy for a single vertical: flights, hotels, cars, or trains. Returns budget limits, booking windows, cabin or star restrictions, and other rules for that vertical. Returns null when the user has no travel policy assigned.
policies_get_travel_policy
Check the status of a report started with `reporting_create_travel_report`. If state is 'running', wait a few seconds and call again — reports typically take 10–60 seconds. When state is 'complete', url contains a presigned S3 link to download the file (valid for a limited time). Stop polling if state is 'error'.
reporting_get_travel_report
Retrieve structured JSON rows from a report started with `reporting_create_travel_report`. Poll this tool (not `reporting_get_travel_report`) until state is 'complete' — reports typically take 10–60 seconds. Stop polling if state is 'error'. Requires `created_at` from `reporting_create_travel_report` — pass it back on every call. The expiry guard applies only when starting or polling for data (no `cursor`); paginated fetches with `cursor` continue an in-progress read and are not re-checked client-side. Looker async results expire ~5 minutes after task creation — start fetching within 4 minutes. Follow `next_cursor` until null for additional pages. Set `limit` on the first call only to control page size (default 2000, max 2000). On the first call, pass the same `fields` as `reporting_create_travel_report`. Scope the report (date range, fields, filters) in `reporting_create_travel_report`. Use `reporting_get_travel_report` when you need a full CSV/XLSX export.
reporting_get_travel_report_data
Get a link the user can open to approve or decline a specific trip. Call this when the user has clearly asked to approve or decline a trip and you have (or can obtain) its trip ID. Do not use for read-only trip details or listing — use `trips_get_trip` or `trips_list_trips` instead. If the user mentions a trip awaiting approval but has not said approve or decline, ask which action they want before calling this tool. Present the returned URL as a markdown hyperlink using the label field as the link text, e.g. [Approve this trip on Perk](url). Never display the raw URL.
approvals_get_trip_approval_links
Generate a Perk checkout deep-link for a selected flight itinerary. Called after the user has confirmed their offer selection from get_flight_leg_options. For one-way trips: first_leg_offer_id and final_leg_offer_id are the same offer. For round-trips: first_leg_offer_id is the outbound offer chosen on the first get_flight_leg_options call, and final_leg_offer_id is the return offer chosen on the second call. Present the returned URL as a markdown hyperlink using the label field as the link text, e.g. [Complete your booking](url). Never display the raw URL.
flights_build_flight_deeplink
List the account's car delivery and collection addresses - locations where a rental car can be delivered to and picked up from, instead of a rental office. Call this before start_car_search when the user wants delivery/collection instead of picking up at a rental office, to offer them the available addresses to choose from. Returns an empty list when the account does not have this feature enabled - in that case, use search_car_places for rental office or airport locations instead. Each returned address is a location object (place_type='car-delivery-address') that can be passed unchanged as the pick_up_location or drop_off_location for start_car_search.
cars_list_car_delivery_addresses
List attendees for a specific Perk Event. Requires the caller to be an event organizer.Returns a paginated list of attendance records with RSVP status and person details.Use search to filter by attendee name or email. Use attending to filter by RSVP state. Default page size is 20; increment page to fetch more results.
events_list_event_attendance
List Perk Events for the authenticated user. Results are already scoped to events the user can see in their account (organizing, invited, attending, or account-visible). By default returns upcoming events. Use date_filter='past' for ended events or date_filter='no_dates' for events without dates. Use status to narrow by role: organizing, attending, or invited.
events_list_events
List individual line items across invoices. Each line represents a single booked service (flight, hotel, car, etc.) and includes trip metadata, traveller, cost center, tax, and amount. Filter by profile, serial number, or date range.
invoices_list_invoice_lines
List the invoice profiles (payment profiles) associated with your Company. Every Invoice is associated to an Invoice Profile. The Invoice Profile holds billing preferences like payment method, billing address, and billing period.
invoices_list_invoice_profiles
List invoices for your company. Supports filtering by profile, status, date range, billing period, and serial number. Returns paginated results with invoice metadata including amounts, due dates, and a PDF download URL on each invoice.
invoices_list_invoices
List the authenticated user's own company cards plus any lodge cards in their company — i.e. the cards they can submit expenses against. Use this when the user asks which cards they have, their card details, or which card to use for an expense. Takes no parameters; the user and company are resolved from their identity. Returns a list of company cards (no pagination). Card fields: - id (str): card identifier - name (str): card name - currency (str): ISO 4217 currency code - status (str): active, inactive, draft, ordered, processing, frozen, or deleted - number (str): last 4 digits of the card number - cardType (str): physical, virtual, singleUse, or lodge - provider (str): Perk, or Other
transactions_list_available_cards
List the authenticated user's pending card transactions — unmatched, open transactions on cards they own that still need to be matched to an expense. Use this when the user asks what card transactions they need to act on, reconcile, or attach receipts to. Takes no parameters; the user and company are resolved from their identity. Lodge-card transactions are excluded. Returns a list of card transactions (all results, no pagination). Transaction fields: - id (str): transaction identifier - transactionDate (str): ISO date (YYYY-MM-DD) - merchant (str): merchant name - amount (number): transaction amount - currency (str): ISO 4217 currency code - cardId (str): id of the card the transaction belongs to - expenseId (str|null): linked expense id; null when unmatched - status (str): unmatched, matched, private, or archived (pending results are always unmatched) - isClosed (bool): always false for pending results - sourceAmount (number|null): original amount in the source currency - sourceCurrency (str|null): ISO 4217 source currency code - expenseStatus (str|null): notReadyForExport, readyForExport, or exported - isProcessing (bool): whether the transaction is still being processed - isCreditNote (bool): whether the transaction is a credit note / refund
transactions_list_pending
List trips for the authenticated user. By default, only returns trips where the authenticated user is a traveler. Set only_as_traveler=false to include trips the user manages (admins/travel managers only). Returns newest trips first by default. Filter by date range, status, or trip content.
trips_list_trips
Query expenses from Spend for the authenticated user. This single tool covers two perspectives, selected by the `role` parameter: - role='submitter' (DEFAULT): the caller's OWN expenses — ones they submitted or are drafting. Use for 'my expenses', 'expenses I still need to submit', etc. - role='approver': expenses awaiting the caller's approval as an approver/manager (queried in the approval view context, viewRole: approval). Use for 'what expenses are waiting for my approval?', 'show me Anna's expense I need to approve', or to review a specific expense before approving it (filter by id). Results are always scoped server-side to the caller in the chosen role — for role='approver' you do NOT need to (and cannot) filter by approver id yourself. When role='approver' and no filters are given, it defaults to expenses awaiting approval/review (status in [2, 3]). Before using role='submitter', clarify whether the user wants their own individual data or org-wide data. Do NOT use this tool for company/account-wide spend, expenses across multiple users or a team, aggregated/summarised reports, or cost objects — use the reporting domain tools for those. Supports flexible filtering, field selection, and sorting. When an explicit `fields` list is requested, the response always also includes `id` (for the MCP-generated `expenseUrl`), digitization progress (isProcessing), the AI (OCR) fields (merchant, totalAmount, currency, expenseDate, taxItems, categoryId), and submitter identity (userFirstName, userLastName, userId) — compose a display name from first+last (fall back to userId). These are limited to expense-service's public query allowlist; receiptPath, attachments, and isPdf cannot be selected on projected queries (omit `fields` if you need the backend's fuller default field set including receipt metadata). Every expense includes an `expenseUrl` that opens its canonical detail page in Perk. Common filter patterns: - My open unsubmitted expenses (submitter): isOpen==true, tripId==null, status in [0,1,9,10] - Awaiting my approval (approver): status in [2,3] (applied by default) - A specific expense to review (approver): id=='<expenseId>' - Date range: expenseDate >= '2026-01-01' AND expenseDate <= '2026-03-31' For role='approver', an empty result is a normal, successful outcome — do NOT retry. With NO filters it means the approval queue is empty (tell the user they have nothing to approve); for a FILTERED approver query (e.g. id=='<expenseId>') it means no expense matching that filter is awaiting your approval (not found), not that the queue is empty.
expenses_query_expenses
Search for airports, cities, and addresses by name, to resolve the place objects required by start_car_search's pick_up_location/drop_off_location parameters. Call this before every car search - these places can only come from here, never from memory or guesswork. Results mix airports and other places (place_type). Prefer an airport result when the user names an airport, and a geocode result for any other place. Pass the chosen result object as pick_up_location or drop_off_location, unchanged.
cars_search_car_places
Search for airports and cities by name or IATA code, to resolve the place objects required by start_flight_search's destinations (origin/destination). Call this before every flight search - these places can only come from here, never from memory, guesswork, or an invented IATA code. Results mix cities and airports (place_type). Prefer an airport result when the user names a specific airport, and a city result when they just name a city (it covers all airports serving that city, e.g. all London airports). Pass the chosen result object as origin or destination within each destinations leg, unchanged.
flights_search_flight_places
Search for train stations and cities by name, to resolve the place objects required by start_train_search's origin/destination parameters. Call this before every train search - these places can only come from here, never from memory or guesswork. Results mix cities and train stations (place_type). Prefer a train_station result when the user names a specific station, and a city result when they just name a city (it resolves to that city's main station). Pass the chosen result object as origin or destination, unchanged.
trains_search_train_places
Search and filter users in your Perk account. Use filter_text to find users by name or email. Combine with state, roles, exclude_roles, user_ids, login_emails, manager_ids, is_manager, company_ids, or cost_center_ids to narrow results server-side. Returns up to 10 users by default; pass limit (max 1000) to retrieve more. Returns user identity and organisational details. FILTERING FOR ABSENCE — several ID-based filters accept -1 as a special sentinel meaning 'no value assigned'. Supported fields: manager_ids=[-1] (users with no line manager), approval_process_ids=[-1] (users with no approval process), cost_center_ids=[-1] (users with no cost center), payment_profile_ids=[-1] (users with no payment profile). Example: to find users with no line manager AND no approval process, pass manager_ids=[-1] and approval_process_ids=[-1].
identity_search_users
Look for higher cabin or fare alternatives to ONE offer the user has already seen in get_flight_leg_options - e.g. 'what would business class cost on that flight?' or 'is there a fare that lets me change it?'. Optional: only call it when the user asks about upgrading a specific offer. FIRST LEG ONLY: the offer must come from the get_flight_leg_options call made with an empty leg_fare_selections, and passing that same empty list here is what confirms it. A return-leg offer is rejected - upgrade the outbound flight before the user picks a return. One call returns every cabin and fare on that flight, so there is no need to ask for a particular one. It rides the existing search rather than starting a new one, so poll get_flight_upsell_options with the (transaction_id, offer_id) pair this returns. Only a few upsells can run at once: finish one before starting another.
flights_start_flight_upsell
Start a flight search. Before calling this tool, always confirm with the user whether they want a one-way or round-trip flight. For round trips, include both the outbound and return legs in destinations. For multi-city trips, include each leg in order. This is the first step in the FLIGHT SEARCH FLOW FLIGHT SEARCH FLOW — Steps, in order: 0. search_flight_places — resolve every origin/destination first, for every leg. Never guess or invent an IATA code. 1. start_flight_search — confirm one-way vs round-trip with the user first. Returns transaction_id, trip_id, and is_international as a best-effort hint (it can be wrong, e.g. for city-level places). The backend begins searching immediately, so pausing after this step costs no search time. 2. check_visa_requirements — ALWAYS call this after every start_flight_search, regardless of is_international, passing the SAME destinations you passed to start_flight_search (for round trips, only the outbound leg). It takes destinations - never call it with no arguments. It independently re-verifies whether the trip is international and silently returns nothing for domestic routes, so calling it is always safe and never wastes the user's time. Do NOT announce to the user that you are checking visa requirements before calling this tool - call it silently and present only the result. 3. get_flight_leg_options — poll with the transaction_id until status is 'complete' or 'error'. For round-trips, get outbound options first, then return options after the user picks an outbound flight. 4. start_flight_upsell / get_flight_upsell_options — OPTIONAL, only when the user asks about a better cabin or fare for an offer they have already seen. Upsell applies to the FIRST (outbound) leg only. Skip both otherwise. 5. build_flight_deeplink — only after the user confirms which offer they want in get_flight_leg_options, or in get_flight_upsell_options if they upgraded.
flights_start_flight_search
Put the rate the user picked on their trip. Call after get_hotel_rates with the same transaction_id and hotel_id, the rate_id and provider_id of the rate they picked, and the trip_id from start_hotel_search. It adds the hotel rate to the trip; nothing is booked or paid. Anything the user asked to pass on to the hotel goes in note_for_hotel, in their own words. - ALWAYS ask the user for a note for the hotel first, unless they have already given you one. - ALWAYS confirm what went on the trip by name: the hotel, the room option, the rate title and its price_total, e.g. 'Added AC Hotel Madrid Feria by Marriott, Twin Room, breakfast included, EUR 542.96'. - ALWAYS present the returned url as a markdown hyperlink with label as the link text, e.g. [Complete your booking on Perk](url). - NEVER pass anything internal on to the user, as a value or in your own words, e.g. rate_id, provider_id, which provider sells the rate, or the raw url.
hotels_select_hotel_rate
Send a single expense awaiting the authenticated user's approval back to its submitter for revision. This transitions the expense to needsRevision and includes a required reason for the submitter. This is not a hard reject. IMPORTANT — this performs a consequential state change. Before calling this tool you MUST: 1. Identify the specific expense (by id) and its submitter, typically via `query_expenses` with role='approver'. 2. Explicitly confirm with the user that they want to send back THAT expense, identifying the submitter and stating the reason that will be shown to them. Do not send back expenses speculatively or in bulk. Authorization is enforced server-side: if the caller is not a current approver, or the expense is no longer pending, the action fails safely. On success the tool returns {expenseId, sentBack: true, expenseUrl}; `expenseUrl` opens the expense in Perk's approver view.
expenses_send_back_expense
Start a car rental search. pick_up_location and drop_off_location must be the whole object returned unchanged from search_car_places or list_car_delivery_addresses - never invented or partially constructed from memory. When drop_off_location is omitted, the same location is used for both the pick-up and drop-off legs. selected_main_driver_id defaults to the first entry in travelers_ids when omitted. Call get_car_search_results next with the returned transaction_id to fetch offers. If this car rental belongs to a Trip that already has another booking or search (for example a train or flight for the same trip), pass that booking's trip_id here so this search joins the same Trip instead of creating a new one. Reuse the exact trip_id returned by the earlier start_train_search, start_flight_search, or start_hotel_search call.
cars_start_car_search
Start fetching the rooms and rates for one hotel. Call after get_hotels with its transaction_id and the hotel_id of the hotel the user picked. This tool returns no rooms or rates - call get_hotel_rates next with the same parameters.
hotels_start_hotel_rates
Start a hotel search. Always creates a new trip. Set location_query to where the user wants to stay, in their own words - a city ('barcelona'), an airport ('barcelona airport'), a station, a neighbourhood, an address, or one named hotel ('Hilton Diagonal Mar'). Include the country or region when the user mentioned one ('cambridge, MA'). Tell the user the fully qualified address the search ran for - they cannot see it otherwise. If it is not what they meant, search again with a more specific location_query. This tool returns no hotels - call get_hotels next with the returned transaction_id.
hotels_start_hotel_search
Start the inbound (return) leg of a round-trip train search. Call this only after the user has selected an outbound offer from get_train_search_results. Returns a new transaction_id for the inbound leg - use it with get_train_search_inbound_results, keeping the outbound transaction_id and offer_id for build_train_deeplink.
trains_start_train_search_inbound
Start a train search. Before calling this tool, always confirm with the user whether they want a one-way or round-trip journey. For a round trip, pass return_date and return_departure_time so both legs are committed to the same search - the backend resolves inbound offers against whichever outbound offer the user later picks, and requires the return leg's date and time to have been committed here; they cannot be supplied later. For round trips, call start_train_search_inbound after the user picks an outbound offer from get_train_search_results. origin and destination take the whole object returned by search_train_places, passed back unchanged - not just the place_id string, and never an invented value. If this train belongs to a Trip that already has another booking or search (for example a car rental or flight for the same trip), pass that booking's trip_id here so this search joins the same Trip instead of creating a new one. Reuse the exact trip_id returned by the earlier start_car_search, start_flight_search, or start_hotel_search call.
trains_start_train_search
Submit one of the authenticated user's expenses for approval, using the same expense-service submit action as the Spend UI. IMPORTANT — before calling this tool you MUST: 1. Resolve the expense via `query_expenses`. 2. Confirm the expense identity (merchant, amount, date) and the user's intent to submit it for approval. 3. If required fields are missing, complete them first via `update_expense` (and `get_expense_form_options` for selectable fields). 4. Prefer user-friendly progress language (e.g. “Submitting your expense for approval”). Trip expenses that are only ready for trip submission must be submitted with the trip, not through this tool. Successful responses include `expenseUrl`, which opens the canonical expense detail page in Perk. If an unsupported custom field prevents submission, the error guidance includes the same URL so the user can complete it in Perk.
expenses_submit_expense
Partially update one of the authenticated user's editable expenses (draft / needs revision / similar), using the same expense-service PATCH contract as the Spend UI. IMPORTANT — before calling this tool you MUST: 1. Resolve the expense via `query_expenses`. 2. ALWAYS call `get_expense_form_options` when changing selectable fields (category, cost center, tags, payment method / company card). Use `include_cards=true` when the user may pick a company card. Present those options to the user in friendly labels/names and confirm their choice before patching. 3. Confirm the intended field changes with the user. 4. Prefer user-friendly progress language (e.g. “Updating the cost center on your draft expense”). Only allowlisted fields are accepted. For `cost_center_items`, send the full intended snapshot (merge with current values from `query_expenses` first — that response includes costCenterItems). For `tags`, send only the dimension→option pairs you want to set or change; this tool merges them onto the expense's current tags so omitted dimensions are preserved (`query_expenses` cannot return the tags id map). Amount/currency edits reuse the expense's frozen FX rates — do not invent rates. Mileage- and per-diem-specific fields are not supported. `total_amount` and `currency` are also rejected for mileage and per-diem expenses (those amounts are calculated by Spend and would be silently ignored); that guidance includes the expense's Perk URL. Successful responses include `expenseUrl`, which opens the canonical expense detail page in Perk.
expenses_update_expense
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Perk alternatives on ChatGPT?
As of 2026-09-18, Perk competes with Allowance, Brex, Expensify, PuraFi Expenses, Ramp, Ramp Data, Tropic in ChatGPT Corporate Spend, Expense & Procurement, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.