Cota is a Portuguese-language shipping desk for small parcels. An operator types a shipment the way they would speak to a colleague — origin, destination, dimensions, and weight — and the app returns live prices from myTNT, FedEx, and UPS side by side, then books the chosen service.
The product is a single chat screen. The user writes something like:
Braga para Paris, 3 pacotes de 600×400×250, um com 13 kg e os outros dois com 8 kg.
The server turns that sentence into a shipment, asks all three carriers at once, and answers in Portuguese with:
If the city name is ambiguous, the chat asks the user to pick a numbered match. If origin is omitted, it uses the address on the logged-in myTNT account (Braga by default). International quotes state that duties, taxes, and customs clearance are outside the quoted price.
Reservation is not the same action on every carrier.
FEDEX_ENV=test, apis-sandbox.fedex.com). It returns a test tracking number. It does not book a production pickup.wwwcie.ups.com) unless the environment is explicitly set to production. It returns a shipment number. If API credentials are missing, the site calculator can still show prices, but booking is refused until the client id, secret, and account number exist.A declared customs value of €100 is attached to international FedEx and UPS test shipments and is not part of the quoted transport price. One booking runs at a time per session.
There is no large language model. parse_message in server.py reads Portuguese (and a few English city names) with regular expressions:
de X para Y, X para Y, or X → YL×W×H, with a heuristic that treats very small numbers as metres and very large ones as millimetres, then stores centimetresPlace names are resolved through the myTNT address search. Alias cities (for example a whole-city Paris code) are preferred when the user named the city, not a postcode.
The app is a small Python process with a static front end. No framework, no database.
| Piece | Role |
|---|---|
server.py | Threaded HTTP server on port 8787. Serves the UI and POST /api/chat and POST /api/book. Holds in-memory sessions keyed by a browser id. Parses messages, fans out quotes, writes the Portuguese summary, and places orders. |
carriers.py | FedEx and UPS HTTP clients: OAuth, rate quotes, service-code mapping, and test-shipment creation. |
sites.py | Playwright fallback that drives the public UPS “Calculate Time and Cost” page and the FedEx rating page in Chrome when account API credentials are not the path in use. The browser window is moved off-screen. |
static/ | Chat UI: index.html, app.js, styles.css, logo, and carrier marks. |
video/antes-depois.html | A 1920×1080 storyboard of the flow from request to booking. |
Quotes for the three carriers run in parallel. A failure on one carrier becomes an error line in that carrier’s block; the other two still appear. Secrets are read from ../tnt.env, ../fedex.env, ../ups.env, and ../.env. They are not written to disk or to the chat.
myTNT logs in at mytnt.tnt.com with the account user, keeps the session cookie, and calls the indicative quote service. Prices are marked as indicative; expedition surcharges may be missing. Dimensions are sent in metres.
FedEx has two quote paths. If FEDEX_USER and FEDEX_PASSWORD are set, Chrome fills the Portuguese FedEx rating site with those portal credentials, so the prices belong to that account and can include a list-rate column. Otherwise, and only when FEDEX_ENV=test points at the sandbox, it uses the Rate API with the API key, secret, and account number. The reply says so when the sandbox rate is in the comparison. Booking always stays on the sandbox.
UPS uses the Rating Shop Time in Transit API when client id, secret, and account are present (test CIE by default, production only if UPS_ENV=production). Without those credentials it scrapes the public Portuguese calculator, notes that the figures are export estimates without a selected account number, and, when pieces have different weights, adds separate quotes together. The public calculator accepts at most 20 pieces.
Both API clients retry once when the carrier rejects the ship date or the postal-code format, and FedEx also retries when an international quote needs a customs clearance block.
The page is titled Cota · expedição, in Portuguese, with the line “Expedição rápida · pequenos volumes”. Two example buttons fill a typical multi-piece and single-piece request. The session id lives in localStorage. The chat posts JSON and renders carrier tables with a reserve action that opens a receiver form: Marcar recolha for myTNT, Criar envio de teste for FedEx and UPS.
The server binds to 0.0.0.0:8787 and prints the localhost URL plus LAN addresses, so another machine on the same network can open the desk. myTNT credentials (TNT_USER, TNT_PASSWORD) are required at startup.
Next Step
- integration with company's ERP to create the transport purchase order with the transporter reference in order to automatically match with the transporter invoice.