Agent Interactions API

The processing core. Every pipeline operation — verification, matching, and rate pushing — is exposed as an async job behind a uniform HTTP 202 → poll interface. Authentication is JWT/JWKS via joserfc; jobs live in Valkey; matching runs against Elasticsearch with OpenAI embeddings.

Endpoints

Method
Path
Purpose
Response
POST
/verify-contract
Verify contract against booking data via GPT.
202 → job
POST
/event-match
Match contract to HotelMap events via ES + embeddings.
202 → job
POST
/dupload
Build and push extracted rates to live HotelMap.
202 → job
POST
/blockload
Build and push offline block rates.
202 → job
POST
/hotel-contact
Extract hotel contact info with geo resolution (geopy).
202 → job
GET
/jobs/{job_id}
Poll job status and result.
200
GET
/health
Health check.
200

Event-match pipeline

Matching is a multi-signal cascade. Cheap heuristics filter candidates before any AI calls; only finalists get embedded for cosine similarity. This keeps OpenAI spend bounded even with hundreds of candidate events.

1 · Extract
Dates · Hotel · City
From the PDF extraction response
2 · Search
Elasticsearch
Relaxed date + hotel/city DSL
3 · Score
Heuristics
Date overlap · Jaccard · domain
4 · Embed
OpenAI
text-embedding-3-large · cosine
5 · Rank
Top-K matches
Weighted combine, return ordered

Pre-flight rejections

Pure functions in rejection.py. They run before any expensive AI call so bad input fails fast and cheaply.

_check_event_date()
Valid event date range exists in the extraction.
_check_cutoff_date()
Cutoff date is not in the past relative to today.
_check_room_counts()
At least one room is defined in the contract.
_check_commission()
Commission falls within reasonable, contract-realistic bounds.

Async job lifecycle

pending
Created by create_job(uuid)
processing
Coroutine running via run_job
completed
Result stored at job:{uuid}
failed
Error, timeout, or stale TTL

Authentication

JWT · JWKS · joserfc
The JWTBearer dependency fetches JWKS (TTL-cached) and decodes incoming Bearer tokens with joserfc. It validates issuer, audience, expiry, and required scope (interactions:read). If AUTH_JWKS_URI is empty, auth becomes a no-op — used in local dev only.

PDF extraction client & security

The API client for the upstream Contract Extractor wraps submission + polling with a transparent Retry-After handler. Security failures are deterministic and surfaced as a clear error hierarchy — never silently retried.

!
ExtractionSecurityError
HTTP 400 — prompt injection, homoglyph attack, or invalid PDF. Triggers a Slack security rejection.
?
ExtractionClassificationError
Layer-2 reject — input is not a hotel contract. The agent learns to skip and reply without burning rate-push attempts.
ExtractionFlaggedError
Layer-4 — suspicious output, review_required. The contract is queued for human review instead of pushed.