{"openapi":"3.1.0","info":{"title":"Energy Connect API — Public","version":"1.0.0","description":"Multi-tenant fuel logistics platform API. Built to the control expectations of SOC 2 and ISO 27001 while we work toward certification (not yet held). Details: https://energyconnecter.ai/security","contact":{"name":"Energy Connect","url":"https://energyconnecter.ai","email":"api@energyconnecter.ai"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://api.energyconnecter.ai/v1/public","description":"Production"},{"url":"https://staging-api.energyconnecter.ai/v1/public","description":"Staging"},{"url":"http://localhost:4000/v1/public","description":"Local development"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"RS256 JWT access token (15min TTL)"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Company API key (SHA-256 hashed)"},"cookieAuth":{"type":"apiKey","in":"cookie","name":"refreshToken","description":"Signed httpOnly refresh token (7d TTL)"}},"schemas":{"CustomerDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ref":{"type":"string","description":"Customer-facing load reference (LD-…)"},"status":{"type":"string","description":"OrderStatus"},"phase":{"type":"string","enum":["scheduled","on_the_way","on_site","delivered","cancelled"]},"distributor":{"type":"object","properties":{"name":{"type":"string"},"logoUrl":{"type":"string","nullable":true}}},"poNumber":{"type":"string","nullable":true},"requestedDate":{"type":"string","format":"date-time","nullable":true},"deliveryDate":{"type":"string","format":"date-time","nullable":true},"deliveryStart":{"type":"string","nullable":true},"deliveryEnd":{"type":"string","nullable":true},"destination":{"type":"object","properties":{"name":{"type":"string","nullable":true},"street":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true}}},"stop":{"type":"object","nullable":true,"description":"Split loads only: the caller's stop","properties":{"sequence":{"type":"integer"}}},"lines":{"type":"array","items":{"type":"object","properties":{"product":{"type":"string"},"unitCode":{"type":"string"},"ordered":{"type":"number"},"loadedNet":{"type":"number","nullable":true},"loadedGross":{"type":"number","nullable":true},"deliveredNet":{"type":"number","nullable":true},"deliveredGross":{"type":"number","nullable":true}}}},"bolNumbers":{"type":"array","items":{"type":"string"}},"eta":{"type":"string","format":"date-time","nullable":true},"arrivedAt":{"type":"string","format":"date-time","nullable":true},"deliveredAt":{"type":"string","format":"date-time","nullable":true},"receiverName":{"type":"string","nullable":true,"description":"EC-4686 — who received the delivery (OrderDelivery.receiverName). Always null on a split load: a stop records no receiver"},"spill":{"type":"object","nullable":true,"description":"EC-4774 — the spill the driver reported at delivery, or null. Always null on a split load: the spill is recorded for the whole truck, not the caller's stop","properties":{"gallons":{"type":"integer","nullable":true},"details":{"type":"string","nullable":true}}},"invoice":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"invoiceNumber":{"type":"string"},"status":{"type":"string"},"total":{"type":"number"},"dueDate":{"type":"string","format":"date-time","nullable":true}}},"createdAt":{"type":"string","format":"date-time"}}},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["order.updated","order.accepted","bid.created","bid.accepted","bid.declined","bid.countered","bid.withdrawn","invoice.issued","invoice.paid","delivery_ticket.signed","bol.submitted","delivery.rejected","invoice.created","invoice.voided","invoice.disputed","connection.updated","terminal_access.updated","pricing.updated"]}},"description":{"type":"string","nullable":true},"isActive":{"type":"boolean"},"lastSuccessAt":{"type":"string","format":"date-time","nullable":true},"lastFailureAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"secret":{"type":"string","description":"Present ONLY in the create and rotate-secret responses — store it; it is never readable again"}}},"WebhookOutboundDeliveryRow":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","SUCCEEDED","DEAD"]},"attempts":{"type":"integer"},"nextAttemptAt":{"type":"string","format":"date-time"},"lastStatusCode":{"type":"integer","nullable":true},"lastError":{"type":"string","nullable":true},"deliveredAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"event":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}},"WebhookEventEnvelope":{"type":"object","description":"The body of every outbound webhook delivery. The request also carries the headers X-EC-Event-Id, X-EC-Delivery-Id, X-EC-Timestamp (unix seconds) and X-EC-Signature: v1=hex(HMAC_SHA256(secret, `${X-EC-Timestamp}.${rawBody}`)). The body contains identifiers only. It never contains the entity. Fetch the entity through the public API, which applies every mask and tier rule. Answer 2xx in 10 seconds. Delivery is at-least-once, so deduplicate on `id`. A retry and a replay send the same `id`. Do not depend on the order of the keys in `data`.","required":["id","type","createdAt","data"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable event id. Deduplicate on this value."},"type":{"type":"string","enum":["order.updated","order.accepted","bid.created","bid.accepted","bid.declined","bid.countered","bid.withdrawn","invoice.issued","invoice.paid","delivery_ticket.signed","bol.submitted","delivery.rejected","invoice.created","invoice.voided","invoice.disputed","connection.updated","terminal_access.updated","pricing.updated"],"description":"The event type you subscribed to."},"createdAt":{"type":"string","format":"date-time","description":"The time the platform recorded the event, not the time of this attempt."},"data":{"description":"Identifiers only. `x-ec-event-data` on this schema gives the exact shape for each event type.","oneOf":[{"$ref":"#/components/schemas/WebhookEventDataOrderStatus"},{"$ref":"#/components/schemas/WebhookEventDataBid"},{"$ref":"#/components/schemas/WebhookEventDataInvoiceStatus"},{"$ref":"#/components/schemas/WebhookEventDataDeliveryTicket"},{"$ref":"#/components/schemas/WebhookEventDataOrder"},{"$ref":"#/components/schemas/WebhookEventDataConnection"},{"$ref":"#/components/schemas/WebhookEventDataTerminalAccess"},{"$ref":"#/components/schemas/WebhookEventDataPricing"}]}},"x-ec-event-data":{"order.updated":"WebhookEventDataOrderStatus","order.accepted":"WebhookEventDataOrderStatus","bid.created":"WebhookEventDataBid","bid.accepted":"WebhookEventDataBid","bid.declined":"WebhookEventDataBid","bid.countered":"WebhookEventDataBid","bid.withdrawn":"WebhookEventDataBid","invoice.issued":"WebhookEventDataInvoiceStatus","invoice.paid":"WebhookEventDataInvoiceStatus","delivery_ticket.signed":"WebhookEventDataDeliveryTicket","bol.submitted":"WebhookEventDataOrder","delivery.rejected":"WebhookEventDataOrder","invoice.created":"WebhookEventDataInvoiceStatus","invoice.voided":"WebhookEventDataInvoiceStatus","invoice.disputed":"WebhookEventDataInvoiceStatus","connection.updated":"WebhookEventDataConnection","terminal_access.updated":"WebhookEventDataTerminalAccess","pricing.updated":"WebhookEventDataPricing"}},"WebhookEventDataOrderStatus":{"type":"object","description":"Body of `order.updated` and `order.accepted`. `order.accepted` always reports the status CONFIRMED.","required":["orderId","status"],"additionalProperties":false,"properties":{"orderId":{"type":"string","description":"Read the order with GET /orders/{orderId}."},"status":{"type":"string","description":"The order status after the change, for example CONFIRMED or DELIVERED."}}},"WebhookEventDataBid":{"type":"object","description":"Body of `bid.created`, `bid.accepted`, `bid.declined`, `bid.countered` and `bid.withdrawn`. The counter rate and the decline reason stay off the wire. Read them from the API.","required":["orderId","bidId"],"additionalProperties":false,"properties":{"orderId":{"type":"string","description":"The order the bid belongs to."},"bidId":{"type":"string","description":"The bid this event reports."}}},"WebhookEventDataInvoiceStatus":{"type":"object","description":"Body of `invoice.created`, `invoice.issued`, `invoice.paid`, `invoice.voided` and `invoice.disputed`. The status is DRAFT or OUTSTANDING for `invoice.created` (an uploaded invoice is born OUTSTANDING), OUTSTANDING for `invoice.issued`, PAID for `invoice.paid`, VOID for `invoice.voided` and DISPUTED for `invoice.disputed`. `invoice.created` goes to the issuer only; the payer hears on `invoice.issued`. The dispute reason stays off the wire. Read it from the API.","required":["invoiceId","status"],"additionalProperties":false,"properties":{"invoiceId":{"type":"string","description":"Read the invoice with GET /invoices/{invoiceId}."},"status":{"type":"string","enum":["DRAFT","OUTSTANDING","PAID","VOID","DISPUTED"],"description":"The invoice status after the change."}}},"WebhookEventDataDeliveryTicket":{"type":"object","description":"Body of `delivery_ticket.signed`.","required":["deliveryTicketId"],"additionalProperties":false,"properties":{"deliveryTicketId":{"type":"string","description":"The signed delivery ticket."}}},"WebhookEventDataOrder":{"type":"object","description":"Body of `bol.submitted` and `delivery.rejected`. `bol.submitted` reports the order, not the document. Read the documents of the order through the API. For `delivery.rejected`, read `delivery.rejectedAt` and `delivery.rejectionReason` on the order.","required":["orderId"],"additionalProperties":false,"properties":{"orderId":{"type":"string","description":"The order that received the bill of lading, or whose delivery was rejected."}}},"WebhookEventDataConnection":{"type":"object","description":"Body of `connection.updated`. Sent to both companies on the connection when a request is sent, accepted or declined, or when the pair disconnects. Read the connection with GET /connections/{connectionId}.","required":["connectionId","status"],"additionalProperties":false,"properties":{"connectionId":{"type":"string","description":"The connection that changed."},"status":{"type":"string","enum":["PENDING","ACCEPTED","DECLINED","DISCONNECTED"],"description":"The connection status after the change. PENDING is a new or re-sent request."}}},"WebhookEventDataTerminalAccess":{"type":"object","description":"Body of `terminal_access.updated`. Sent to the supplier that owns the terminal and to each company with an accepted connection to it (same demo/live tier) that the supplier has approved for that terminal. A newly created terminal has no approvals yet, so its created event reaches the supplier only. The supplier reads the terminal with GET /supplier-terminals/{terminalId}; that read serves the owner only, so a connected company uses the id to match the terminal on its side.","required":["terminalId","action"],"additionalProperties":false,"properties":{"terminalId":{"type":"string","description":"The supplier terminal that changed."},"action":{"type":"string","enum":["created","updated","deactivated"],"description":"What happened to the terminal. A deactivated terminal stays readable by id with isActive false."}}},"WebhookEventDataPricing":{"type":"object","description":"Body of `pricing.updated`. The price itself stays off the wire. `kind: \"rack\"` reports a rack price change at one terminal: one event per terminal per save or import, sent to the supplier, and, for a published price already in effect, to each company with an accepted connection to the supplier that can read that price (same demo or live tier, past any pricing subscription, and approved for that terminal; the master list needs no terminal approval). The supplier re-reads GET /pricing/terminal/{terminalId}, or GET /pricing/rack when terminalId is null. `kind: \"contract\"` reports a contract price change, sent to the supplier and the named customer only. Read it with GET /pricing/contract. A future-dated price reaches the supplier only, and no event fires later when it takes effect.","required":["kind","supplierCompanyId","customerCompanyId","terminalId","effectiveAt"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["rack","contract"],"description":"Which price changed."},"supplierCompanyId":{"type":"string","description":"The supplier whose price changed."},"customerCompanyId":{"type":"string","nullable":true,"description":"The customer a contract price is for. Null for a rack price."},"terminalId":{"type":"string","nullable":true,"description":"The terminal of a rack price: a supplier terminal id, or the terminal code a bulk rack feed used. Null for the master price list and for a contract price."},"effectiveAt":{"type":"string","format":"date-time","description":"The earliest effective time among the prices this change saved for that terminal or contract. Re-read prices effective from then."}}},"InvoiceAmendment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceId":{"type":"string","format":"uuid"},"proposedByCompanyId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","APPROVED","DECLINED","WITHDRAWN"]},"proposedChanges":{"type":"object","nullable":true,"description":"The allowlisted invoice fields this amendment proposes"},"proposeNotes":{"type":"string","nullable":true},"priorSnapshot":{"type":"object","nullable":true,"description":"Set on approval — the invoice state the amendment replaced (audit trail)"},"respondedAt":{"type":"string","format":"date-time","nullable":true},"responseNotes":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreatePartyRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":200},"contactName":{"type":"string","maxLength":200},"contactEmail":{"type":"string","format":"email","maxLength":200},"contactPhone":{"type":"string","maxLength":50},"phone":{"type":"string","maxLength":50,"description":"Company main line; falls back to contactPhone"},"street":{"type":"string","maxLength":200},"city":{"type":"string","maxLength":100},"state":{"type":"string","maxLength":100},"zip":{"type":"string","maxLength":20},"country":{"type":"string","maxLength":120},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"externalId":{"type":"string","maxLength":120,"description":"Caller's own id for this party"},"notes":{"type":"string","maxLength":2000}}},"NetworkParty":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["CUSTOMER","SUPPLIER"]},"connectionId":{"type":"string","format":"uuid"},"connectionStatus":{"type":"string","example":"ACCEPTED"}}},"UpsertTerminalRequest":{"type":"object","required":["terminalCode","name"],"properties":{"terminalCode":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":200},"city":{"type":"string","maxLength":100,"nullable":true},"state":{"type":"string","maxLength":100,"nullable":true},"zip":{"type":"string","maxLength":20,"nullable":true},"country":{"type":"string","maxLength":120,"nullable":true},"countryCode":{"type":"string","maxLength":2,"nullable":true},"region":{"type":"string","maxLength":120,"nullable":true},"lat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"lng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"capacity":{"type":"integer","minimum":0,"nullable":true},"operatingHours":{"type":"string","maxLength":500,"nullable":true},"unitSystem":{"type":"string","enum":["gal","liter","bbl"],"default":"gal"},"taxingAuthority":{"type":"string","maxLength":200,"nullable":true},"isActive":{"type":"boolean","default":true}}},"PublicOrderNote":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"orderId":{"type":"string","format":"uuid"},"content":{"type":"string"},"authorName":{"type":"string","nullable":true,"description":"Resolved display name of the note author"},"createdAt":{"type":"string","format":"date-time"}}},"Error":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string","example":"Validation failed"},"details":{"type":"object","additionalProperties":true}}}}},"RateLimitError":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["RATE_LIMIT_EXCEEDED"],"example":"RATE_LIMIT_EXCEEDED"},"kind":{"type":"string","enum":["burst","quota"],"example":"burst","description":"Present on every `/v1/public/*` 429. `burst` = per-minute limit: back off until `resetAt`. `quota` = monthly cap exhausted: retrying will not succeed before `resetAt` (first of next month, UTC). Branch on this, not on `Retry-After` magnitude."},"message":{"type":"string","example":"Too many requests. Please slow down."},"retryAfterSec":{"type":"integer","example":42},"resetAt":{"type":"string","format":"date-time","example":"2026-10-01T00:00:00.000Z"}}}}},"PaginatedResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"},"totalPages":{"type":"integer"},"hasMore":{"type":"boolean"}}}}},"Order":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["DRAFT","POSTED","BIDDING","ASSIGNED","CONFIRMED","IN_TRANSIT","DELIVERED","PENDING_VERIFICATION","INVOICED","CANCELLED","PETITIONED","PETITION_DECLINED"]},"orderType":{"type":"string","enum":["direct","bid"],"default":"direct"},"pricingType":{"type":"string","enum":["fixed","bid"],"default":"fixed"},"urgency":{"type":"string","enum":["routine","urgent","emergency"],"default":"routine"},"notes":{"type":"string","nullable":true},"requirements":{"type":"object","nullable":true,"properties":{"truckType":{"type":"string"},"hazmat":{"type":"boolean"},"twic":{"type":"boolean"},"tankerEndorsement":{"type":"boolean"},"pumpOff":{"type":"boolean"}}},"requestedDate":{"type":"string","format":"date-time","nullable":true},"deliveryDate":{"type":"string","format":"date-time","nullable":true},"deliveryStart":{"type":"string","nullable":true},"deliveryEnd":{"type":"string","nullable":true},"carrierCompanyId":{"type":"string","format":"uuid","nullable":true},"maxRate":{"type":"number","nullable":true},"bidDeadline":{"type":"string","format":"date-time","nullable":true},"loadingNumber":{"type":"string","nullable":true},"poNumber":{"type":"string","nullable":true},"destinationName":{"type":"string","nullable":true,"description":"End-customer / site name. Returned on the orders your key posted, and on every ACCEPTED haul read (past the unaccepted ASSIGNED offer stage, not cancelled) — acceptance ends the disintermediation masking. Null on the load board, on bid reads, and on unaccepted offers."},"destinationStreet":{"type":"string","nullable":true},"destinationCity":{"type":"string","nullable":true},"destinationState":{"type":"string","nullable":true},"destinationZip":{"type":"string","nullable":true},"destinationLat":{"type":"number","nullable":true},"destinationLng":{"type":"number","nullable":true},"fullDestinationAddress":{"type":"string","nullable":true,"description":"Preformatted \"street, city, ST zip\" line merging the destination fields (location fallback)."},"shipToContact":{"$ref":"#/components/schemas/ShipToContact","description":"The ship-to's on-site point of contact for this order's delivery address. Present on the order poster's OWN reads whenever the ship-to resolves a contact — their customer, their connection, their roster, so no trust flag is involved — and beyond that disclosed only to a partner the poster has flagged as TRUSTED on their connection. Otherwise, and whenever the ship-to resolves nobody, the key is ABSENT from the payload — not null, not an empty object — so its presence is itself the disclosure signal. On a SPLIT LOAD the poster's own reads omit this order-level field: the order's ship-to pair belongs to at most one drop, so the contacts ride deliveryStops[] instead. Resolved at read time from the poster's ship-to roster, so a contact edited on their side is reflected on the next poll of an in-flight haul."},"acceptedEta":{"type":"string","format":"date-time","nullable":true},"computedEta":{"type":"string","format":"date-time","nullable":true,"description":"Live ETA to the destination, recomputed from the driver's position while the load is in transit. Null until first computed, null once the order is delivered or cancelled, and always null on role=carrier reads (originator reads only)."},"computedEtaAt":{"type":"string","format":"date-time","nullable":true,"description":"When computedEta was last recomputed. Null whenever computedEta is null, and always null on role=carrier reads."},"hasPod":{"type":"boolean","nullable":true},"isExpired":{"type":"boolean","nullable":true},"isExtraction":{"type":"boolean","description":"Order pulls product OUT of a tank rather than delivering it. Post one by sending negative line quantities; the API stores them positive and sets this flag. EC-3717 — a delivery that turns into a pump-out after the fact sets it instead by sending isExtraction on the delivery write (POST/PATCH /orders/{id}/delivery), where the gallons stay positive magnitudes. Not available on a split load, whose direction is per stop (a negative allocation)."},"isSplitLoad":{"type":"boolean","description":"One pickup, several delivery stops. false on an ordinary order."},"creditApprovalRequired":{"type":"boolean","description":"The order is for a customer your company has on credit hold. Publish and assign return 409 CREDIT_APPROVAL_REQUIRED until an admin or owner approves it in the portal."},"creditApprovedAt":{"type":"string","format":"date-time","nullable":true,"description":"When an admin or owner approved this credit-hold order. Null when not approved or not required."},"deliveryStops":{"type":"array","description":"Split-load drops, in route order — each one a self-contained delivery: its own customer, address, per-product allocations (with the allocation ids the per-stop delivery submission uses) and tank fills. Returned to the order originator on GET /orders/{id} (their list rows return [] — stop addresses are a detail-read cost the list does not pay), and to the ASSIGNED carrier on BOTH role=carrier list and detail, which are the same query. A carrier reads its own drops MASKED while the haul is an unaccepted offer (status ASSIGNED, or CANCELLED): destinationName is null and destinationStreet/Zip/Lat/Lng are withheld — city and state stay, so the offer can still be priced. Accepting the haul reveals both, per stop, the same way it reveals the order-level customer identity. A carrier never sees the stops of a haul it is not assigned to. lines[].allocations IS populated for every role on both list and detail, so the shape of the split is readable without the detail call. Empty on a non-split order.","items":{"$ref":"#/components/schemas/OrderDeliveryStop"}},"deliveryProgress":{"type":"object","nullable":true,"description":"How far through its drops a split load is, derived from the stop rows. There is no PARTIALLY_DELIVERED order status — a half-delivered split load still reports status IN_TRANSIT — so this block is the only way to tell a partly-dropped load from an untouched one. Returned wherever deliveryStops is: GET /orders/{id} for the originator, and both role=carrier reads for the assigned carrier. Null on originator list rows, which do not carry the stops — call the detail endpoint. A non-split order reports total 0.","properties":{"total":{"type":"integer","description":"Stops on the order. 0 on an ordinary order."},"resolved":{"type":"integer","description":"Stops that are DELIVERED or SKIPPED."},"pending":{"type":"integer","description":"Stops still PENDING."},"isPartiallyDelivered":{"type":"boolean","description":"At least one stop resolved AND at least one still pending."},"allResolved":{"type":"boolean","description":"Every stop resolved — the order moves to PENDING_VERIFICATION. False when there are no stops at all."}}},"carrier":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"offPlatformCarrierName":{"type":"string","nullable":true,"description":"The hauler your company named for a past delivery when it is not on Energy Connect (free text). carrier and carrierCompanyId are null on those orders; null on every other order. A real carrier is always in carrier."},"location":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true,"description":"null on carrier-role reads (end-customer identity is masked)"},"street":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true}}},"pickup":{"$ref":"#/components/schemas/OrderPickup"},"delivery":{"$ref":"#/components/schemas/OrderDelivery"},"documents":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/OrderDocument"}},"bids":{"type":"array","nullable":true,"description":"Originator view only; [] on carrier-role reads.","items":{"$ref":"#/components/schemas/OrderBid"}},"lines":{"type":"array","items":{"$ref":"#/components/schemas/OrderLine"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"OrderPickup":{"type":"object","nullable":true,"properties":{"bolNumber":{"type":"string","nullable":true},"loadingTicket":{"type":"string","nullable":true},"sealNumbers":{"type":"array","items":{"type":"string"},"nullable":true},"gallonsLoaded":{"type":"number","format":"double","nullable":true},"cardInTime":{"type":"string","nullable":true},"cardOutTime":{"type":"string","nullable":true},"pickupNotes":{"type":"string","nullable":true},"confirmedAt":{"type":"string","format":"date-time","nullable":true}}},"OrderDelivery":{"type":"object","nullable":true,"properties":{"gallonsDelivered":{"type":"number","format":"double","nullable":true,"description":"NET delivered, order-level total."},"grossDelivered":{"type":"number","format":"double","nullable":true,"description":"GROSS delivered, order-level total — the temperature-uncorrected twin of gallonsDelivered. Send it alongside gallonsDelivered on the delivery write; if you send per-asset rows instead, this is their summed gross. Null means never reported (never zero, never inferred from net). Delivered figures are driver-reported actuals, so net may legally exceed gross."},"deliveryTicket":{"type":"string","nullable":true},"sealsIntact":{"type":"string","nullable":true,"enum":["yes","broken","missing"],"description":"Optional. Seal condition on delivery: \"yes\" (intact), \"broken\", or \"missing\". Case-insensitive; true/\"true\"/\"intact\"/\"sealed\" normalize to \"yes\". Omit or send null when the load carries no seals."},"meterBefore":{"type":"number","nullable":true},"meterAfter":{"type":"number","nullable":true},"stickIn":{"type":"string","nullable":true},"stickOut":{"type":"string","nullable":true},"receiverName":{"type":"string","nullable":true},"receiverTitle":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"variance":{"type":"number","nullable":true},"varianceNote":{"type":"string","nullable":true},"spillageOccurred":{"type":"boolean","nullable":true},"spillageDetails":{"type":"string","nullable":true},"spillageGallons":{"type":"integer","nullable":true},"pumpOffUsed":{"type":"boolean","nullable":true},"pumpOffGallons":{"type":"integer","nullable":true},"pumpOffProduct":{"type":"string","nullable":true},"arrivedAt":{"type":"string","nullable":true},"submittedAt":{"type":"string","format":"date-time","nullable":true},"verifiedAt":{"type":"string","format":"date-time","nullable":true},"rejectedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the poster last rejected this delivery and sent it back to the carrier. Null when it was never rejected, or once the carrier has corrected it. A DELIVERED order with rejectedAt set is waiting for the carrier to correct it, not accepted. The delivery.rejected webhook fires at the same moment."},"rejectionReason":{"type":"string","nullable":true,"description":"The poster's reason for the rejection, with the end customer's name removed. Set and cleared together with rejectedAt."},"completedAt":{"type":"string","nullable":true},"assetDeliveries":{"type":"array","items":{"type":"object"},"nullable":true,"description":"Per-asset delivered net/gross/stick readings. Rows come in two kinds and BOTH may carry lineId (from lines[].id), which associates the delivered quantities with that product. assetKey identifies the TANK a row delivered into. What distinguishes the two kinds is the presence of notDelivered: a row that states it (a real boolean, true or false) is a product-level refusal claim and decides that line — true forces the line to 0 delivered so it drops off the customer invoice instead of billing its ordered quantity (the haul charge still counts it), false clears a refusal recorded earlier. A row that OMITS notDelivered is a per-tank breakdown row; tank rows omit it even when their net is 0, because one tank gauging empty is not a refused product. The tank rows of one line are summed, and a positive total clears a stale refusal."}}},"ShipToContact":{"type":"object","description":"A named human at the delivery site. Trusted-partner disclosure only — see the fields that reference this schema.","properties":{"name":{"type":"string"},"title":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}}},"OrderDeliveryStop":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sequence":{"type":"integer","minimum":1,"description":"Route order, 1-based."},"destinationName":{"type":"string","nullable":true,"description":"End-customer / site name for THIS drop. Returned on orders your company posted. On role=carrier reads it follows the acceptance rule the order-level customer identity follows: null while the haul is an unaccepted offer, revealed once the haul is accepted."},"destinationStreet":{"type":"string","nullable":true,"description":"Null on a role=carrier read of an unaccepted offer (status ASSIGNED or CANCELLED) — accepting the haul reveals it."},"destinationCity":{"type":"string","nullable":true},"destinationState":{"type":"string","nullable":true},"destinationZip":{"type":"string","nullable":true,"description":"Withheld pre-acceptance on role=carrier reads, like destinationStreet."},"destinationLat":{"type":"number","nullable":true,"description":"Withheld pre-acceptance on role=carrier reads, like destinationStreet."},"destinationLng":{"type":"number","nullable":true,"description":"Withheld pre-acceptance on role=carrier reads, like destinationStreet."},"status":{"type":"string","enum":["PENDING","DELIVERED","SKIPPED"]},"deliveredAt":{"type":"string","format":"date-time","nullable":true,"description":"When the driver resolved this stop. Null while PENDING, and null on a SKIPPED stop — a skipped stop was never delivered."},"skipReason":{"type":"string","nullable":true,"description":"Why the driver skipped this stop. Always present on a SKIPPED stop (the write path requires it); null otherwise."},"shipToContact":{"$ref":"#/components/schemas/ShipToContact","description":"The on-site point of contact for THIS drop's ship-to. Same disclosure rule as the order-level field: present on the poster's own reads whenever this drop's ship-to resolves a contact, otherwise only to a trusted partner, and absent (never null) for everyone else."},"allocations":{"type":"array","description":"What is dropped here, per product — each row carries its product and the allocation `id` the per-stop delivery submission (POST /orders/{id}/delivery with deliveryStopId) and the 36h stopAllocations edit address.","items":{"$ref":"#/components/schemas/OrderLineAllocation"}},"assets":{"type":"array","description":"The tanks/assets planned to be filled at THIS drop, from the order's per-tank breakdown. Planned figures, gallon-domain. [] when the poster captured no breakdown.","items":{"type":"object","properties":{"name":{"type":"string","nullable":true,"description":"Tank display name (falls back to the poster's asset key)."},"product":{"type":"string","nullable":true,"description":"Product on the line this fill belongs to."},"gallons":{"type":"integer"}}}}}},"OrderLineAllocation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","nullable":true,"description":"The allocation row id — send it back as `allocationId` when submitting this row via POST /orders/{id}/delivery with deliveryStopId, or when editing via PATCH stopAllocations[]."},"deliveryStopId":{"type":"string","format":"uuid"},"product":{"type":"string","nullable":true,"description":"Product of the order line this allocation belongs to — each row is self-contained; no join against lines[] needed."},"plannedQuantity":{"type":"number","description":"Planned quantity for this stop, SIGNED: positive = deliver to the stop, negative = pump OUT of it (a transfer/extraction stop). Per line these NET to the line quantity — a pure transfer (-800 and +800) nets to a line quantity of 0."},"deliveredQuantity":{"type":"number","nullable":true,"description":"Driver actual; null until the stop is delivered."},"deliveredGrossQuantity":{"type":"number","nullable":true,"description":"GROSS delivered actual — optional temperature-uncorrected twin of deliveredQuantity. Never cross-validated against net."},"unitCode":{"type":"string","enum":["gal","liter","bbl"]}}},"OrderDocument":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"docType":{"type":"string","nullable":true},"fileName":{"type":"string","nullable":true},"fileSize":{"type":"integer","nullable":true},"uploadedAt":{"type":"string","format":"date-time","nullable":true},"fileUrl":{"type":"string","nullable":true,"description":"Short-lived signed URL — expires; do not persist."},"downloadUrl":{"type":"string","description":"Stable API path; persist this and follow the 302."}}},"OrderBid":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"rate":{"type":"number","nullable":true},"serviceFee":{"type":"number","nullable":true},"message":{"type":"string","nullable":true},"counterRate":{"type":"number","nullable":true},"counterMessage":{"type":"string","nullable":true},"connectionApproved":{"type":"boolean","nullable":true},"estimatedLoadTime":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"carrier":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}}}},"OrderLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"orderId":{"type":"string","format":"uuid"},"product":{"type":"string"},"productCode":{"type":"string","nullable":true,"description":"EC-4869 — the product master id (e.g. DSL-ULSD-2) for this line's product, resolved by exact match on the poster's product mapping or the product taxonomy. Null when there is no exact match; fall back to `product`."},"terminal":{"type":"string","nullable":true},"terminalCode":{"type":"string","nullable":true,"description":"EC-4869 — the IRS terminal control number of the line's terminal, the same terminalCode GET /master-terminals returns. Null when the terminal cannot be identified with certainty; fall back to `terminal`."},"supplierId":{"type":"string","format":"uuid","nullable":true},"quantity":{"type":"integer","minimum":0,"description":"Stored positive (extraction direction lives on isExtraction). 0 only on a split-load transfer line — the signed net of its allocations."},"gallons":{"type":"integer","minimum":0,"description":"Legacy alias for `quantity`."},"rate":{"type":"number","minimum":0},"buyPrice":{"type":"number","nullable":true},"sellPrice":{"type":"number","nullable":true},"unitCode":{"type":"string","enum":["gal","liter","bbl"],"default":"gal"},"unit":{"type":"string","enum":["gal","liter","bbl"],"default":"gal","description":"Legacy alias for `unitCode`."},"loadingNumber":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"allocations":{"type":"array","description":"This line's split-load drops. Returned wherever the line is (list and detail); [] on a non-split order. The stop ADDRESSES they name are on GET /orders/{id} only.","items":{"$ref":"#/components/schemas/OrderLineAllocation"}},"assets":{"type":"array","description":"The tanks/assets planned to be filled from this line on a single-stop order, from the order's per-tank breakdown. Planned figures, gallon-domain. [] when the poster captured no breakdown, and [] on a split load, whose fills are on deliveryStops[].assets.","items":{"type":"object","properties":{"name":{"type":"string","nullable":true,"description":"Tank display name (falls back to the poster's asset key)."},"product":{"type":"string","nullable":true,"description":"Product on this line."},"gallons":{"type":"number"}}}}}},"CreateOrderRequest":{"type":"object","required":["lines"],"description":"Creates a DRAFT. Send it with POST /orders/{id}/publish — a carrier-addressed order (carrierCompanyId) becomes an offer to that carrier; otherwise it goes to the load board. carrierCompanyId and brokerCompanyId are mutually exclusive.","properties":{"locationId":{"type":"string","format":"uuid","nullable":true,"description":"Saved delivery Location; alternatively pass the destination* fields inline."},"orderType":{"type":"string","enum":["direct","bid","internal_bid"],"default":"direct"},"pricingType":{"type":"string","enum":["fixed","bid"],"default":"fixed"},"urgency":{"type":"string","enum":["routine","urgent","emergency"],"default":"routine"},"notes":{"type":"string","maxLength":2000,"nullable":true},"requirements":{"type":"object","nullable":true,"properties":{"truckType":{"type":"string","maxLength":50},"hazmat":{"type":"boolean"},"twic":{"type":"boolean"},"tankerEndorsement":{"type":"boolean"},"pumpOff":{"type":"boolean"},"loadPosition":{"type":"string","enum":["top","bottom"]},"ftl":{"type":"string","enum":["yes","no","partial"]},"ftlPartial":{"type":"string","maxLength":200},"vaporRecovery":{"type":"boolean"},"coastGuard":{"type":"boolean"},"msha":{"type":"boolean"},"customReqs":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":120}}}},"requestedDate":{"type":"string","format":"date-time","nullable":true},"deliveryDate":{"type":"string","format":"date-time","nullable":true},"deliveryStart":{"type":"string","maxLength":10,"nullable":true},"deliveryEnd":{"type":"string","maxLength":10,"nullable":true},"carrierCompanyId":{"type":"string","format":"uuid","nullable":true,"description":"Direct-send target: on publish the order becomes an offer to this carrier."},"brokerCompanyId":{"type":"string","format":"uuid","nullable":true,"description":"Forward through a broker instead of a carrier. Mutually exclusive with carrierCompanyId."},"maxRate":{"type":"number","minimum":0,"nullable":true},"bidDeadline":{"type":"string","format":"date-time","nullable":true},"bidInvitees":{"type":"array","maxItems":50,"items":{"type":"string","format":"uuid"},"description":"Carrier company ids invited to bid (orderType bid)."},"loadingNumber":{"type":"string","maxLength":50,"nullable":true},"poNumber":{"type":"string","maxLength":64,"nullable":true},"destinationName":{"type":"string","maxLength":160,"nullable":true,"description":"End-customer / site name. Returned on reads of the orders your key posted; masked on carrier/broker-facing reads (see Order.destinationName)."},"destinationStreet":{"type":"string","maxLength":500,"nullable":true},"destinationCity":{"type":"string","maxLength":120,"nullable":true},"destinationState":{"type":"string","maxLength":120,"nullable":true},"destinationZip":{"type":"string","maxLength":32,"nullable":true},"destinationLat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"destinationLng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"deliveryConnectionId":{"type":"string","format":"uuid","nullable":true,"description":"Deliver-to connection for the order. Must be an ACCEPTED connection held by your own company family (your company or its parent); anything else is rejected with 400."},"deliveryAddressId":{"type":"string","maxLength":64,"nullable":true},"notifyContacts":{"type":"array","maxItems":500,"nullable":true,"items":{"type":"object","required":["email"],"properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","maxLength":255}}},"description":"Extra emails notified on order events."},"documents":{"type":"array","maxItems":20,"items":{"type":"object","required":["fileUploadId"],"properties":{"fileUploadId":{"type":"string","format":"uuid"},"name":{"type":"string","maxLength":300}}},"description":"Attach already-uploaded files at create time (see the upload flow)."},"isSplitLoad":{"type":"boolean","default":false,"description":"One pickup, several drops. Requires deliveryStops (2–10) and an allocation on every line; all lines must name the SAME terminal (v1 does not support multi-terminal pickup)."},"deliveryStops":{"type":"array","minItems":2,"maxItems":10,"description":"Split-load drops in route order. Required when isSplitLoad is true. sequence runs 1..N matching array position — no gaps, no duplicates. One stop is not a split load.","items":{"type":"object","required":["sequence"],"properties":{"sequence":{"type":"integer","minimum":1,"maximum":10,"description":"1-based route position; must equal the array index + 1."},"destinationName":{"type":"string","maxLength":160,"nullable":true,"description":"End-customer / site name (write-only on this surface — stop reads never return it, matching the order-level mask)."},"destinationStreet":{"type":"string","maxLength":500,"nullable":true},"destinationCity":{"type":"string","maxLength":120,"nullable":true},"destinationState":{"type":"string","maxLength":120,"nullable":true},"destinationZip":{"type":"string","maxLength":32,"nullable":true},"destinationLat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"destinationLng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"deliveryConnectionId":{"type":"string","format":"uuid","nullable":true,"description":"Per-stop customer connection. Must be an ACCEPTED connection held by your own company family (your company or its parent); anything else is rejected with 400."},"deliveryAddressId":{"type":"string","maxLength":64,"nullable":true},"requestedDate":{"type":"string","format":"date-time","nullable":true,"description":"Per-stop delivery window."},"deliveryStart":{"type":"string","maxLength":10,"nullable":true},"deliveryEnd":{"type":"string","maxLength":10,"nullable":true}}}},"lines":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["product"],"properties":{"product":{"type":"string","maxLength":100},"terminal":{"type":"string","maxLength":200,"nullable":true},"supplierId":{"type":"string","format":"uuid","nullable":true,"description":"EC-3748 — on create this must be a company you hold an accepted connection with, or your own company id (self-supply); anything else is a 400 'Supplier must reference one of your accepted connections'. An id that does not exist and an id belonging to someone else are deliberately indistinguishable. Update, forward, duplicate and recurring are unchanged."},"supplierName":{"type":"string","maxLength":200,"nullable":true,"description":"Ad-hoc supplier: a name for a supplier that is not a company on the platform. Send INSTEAD of supplierId/supplierIds — sending both is a 400, because a name has no id to rank or select among. Creates no company and no connection; it records what happened. Reads emit it as supplier.name with a null supplier.id, and supplierSetBy reports who provided it."},"supplierIds":{"type":"array","maxItems":6,"items":{"type":"string","format":"uuid"},"description":"Ranked supplier candidates (index 0 = primary); no duplicates. Omit when sending supplierName. EC-3748 — on create this must be a company you hold an accepted connection with, or your own company id (self-supply); anything else is a 400 'Supplier must reference one of your accepted connections'. An id that does not exist and an id belonging to someone else are deliberately indistinguishable. Update, forward, duplicate and recurring are unchanged."},"quantity":{"type":"integer","description":"Canonical. One of `quantity`/`gallons` is required. NEGATIVE on every line posts the order as an extraction (stored positive + isExtraction:true). On a split load the value is the signed NET of the line’s allocations (deliveries minus pump-outs) and must not be negative; 0 is legal only on a transfer line carrying a negative allocation. Zero is rejected everywhere else."},"gallons":{"type":"integer","description":"Legacy alias for `quantity` — accepted, but `quantity` wins if both are sent. One of `quantity`/`gallons` is required."},"rate":{"type":"number","minimum":0,"default":0},"buyPrice":{"type":"number","nullable":true},"sellPrice":{"type":"number","nullable":true},"unitCode":{"type":"string","enum":["gal","liter","bbl"],"default":"gal"},"unit":{"type":"string","enum":["gal","liter","bbl"],"default":"gal","description":"Legacy alias for `unitCode` — accepted, but `unitCode` wins if both are sent."},"loadingNumber":{"type":"string","maxLength":120,"nullable":true},"lat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"lng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"assetFills":{"type":"array","maxItems":50,"nullable":true,"items":{"type":"object","required":["assetKey","gallons"],"properties":{"assetKey":{"type":"string","maxLength":200},"name":{"type":"string","maxLength":200},"gallons":{"type":"number","minimum":0,"multipleOf":0.001},"deliveryStopIndex":{"type":"integer","minimum":0,"maximum":9,"description":"Split-load only: which deliveryStops entry this tank sits at. Required on every fill of a split-load line (a tank has one physical location); per stop the fills sum to that line-stop allocation."}}},"description":"Per-tank split; gallons must sum exactly to the line gallons."},"allocations":{"type":"array","maxItems":10,"description":"Split-load only: how this line's quantity divides across the stops. Every line on a split load needs at least one, plannedQuantity must NET exactly to the line quantity (deliveries minus pump-outs), and one line hits a given stop at most once.","items":{"type":"object","required":["deliveryStopIndex","plannedQuantity"],"properties":{"deliveryStopIndex":{"type":"integer","minimum":0,"maximum":9,"description":"Zero-based INDEX into deliveryStops, not a stop id — the ids do not exist until the create transaction inserts the stops."},"plannedQuantity":{"type":"number","multipleOf":0.001,"description":"Planned quantity for this stop, in the line unit, SIGNED: positive = deliver to the stop, NEGATIVE = pump OUT of it (transfer/extraction stop — e.g. -800 extracted at stop 1, +800 delivered at stop 2). Zero is rejected — a stop that gets nothing belongs off the load."}}}}}}}}},"UpdateOrderRequest":{"type":"object","description":"Partial update (DRAFT/POSTED orders) — all CreateOrderRequest fields, each optional.","properties":{"locationId":{"type":"string","format":"uuid","nullable":true},"orderType":{"type":"string","enum":["direct","bid","internal_bid"]},"urgency":{"type":"string","enum":["routine","urgent","emergency"]},"notes":{"type":"string","maxLength":2000,"nullable":true},"requestedDate":{"type":"string","format":"date-time","nullable":true},"deliveryDate":{"type":"string","format":"date-time","nullable":true},"carrierCompanyId":{"type":"string","format":"uuid","nullable":true},"poNumber":{"type":"string","maxLength":64,"nullable":true},"destinationName":{"type":"string","maxLength":160,"nullable":true},"destinationStreet":{"type":"string","maxLength":500,"nullable":true},"destinationCity":{"type":"string","maxLength":120,"nullable":true},"destinationState":{"type":"string","maxLength":120,"nullable":true},"destinationZip":{"type":"string","maxLength":32,"nullable":true},"destinationLat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"destinationLng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"lines":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/CreateOrderRequest/properties/lines/items"}}}},"Invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"orderId":{"type":"string","format":"uuid","nullable":true},"invoiceNumber":{"type":"string"},"status":{"type":"string","enum":["DRAFT","OUTSTANDING","PAID","OVERDUE","DISPUTED","VOID"]},"subtotal":{"type":"number"},"tax":{"type":"number"},"total":{"type":"number"},"fuelCreditAmount":{"type":"number","nullable":true,"description":"Extraction fuel credit reported by the carrier for gallons pumped out. Displayed as its own line; deliberately NOT included in subtotal, tax, or total."},"dueDate":{"type":"string","format":"date-time","nullable":true},"paidAt":{"type":"string","format":"date-time","nullable":true},"issuedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the invoice left DRAFT. Null on a DRAFT. Not the email time (that is sentAt)."},"notes":{"type":"string","nullable":true},"payerCompanyId":{"type":"string","format":"uuid","nullable":true},"contractNumber":{"type":"string","nullable":true},"poNumber":{"type":"string","nullable":true},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLineItem"}},"taxLines":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceTaxLine"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"InvoiceLineItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceId":{"type":"string","format":"uuid"},"description":{"type":"string"},"product":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"gallons":{"type":"integer","nullable":true},"grossGallons":{"type":"integer","nullable":true},"netGallons":{"type":"integer","nullable":true},"rate":{"type":"number","nullable":true},"amount":{"type":"number"},"spec":{"type":"string","nullable":true},"rackPrice":{"type":"number","nullable":true},"margin":{"type":"number","nullable":true},"bolNumber":{"type":"string","nullable":true},"deliveryTicket":{"type":"string","nullable":true},"deliveredDate":{"type":"string","format":"date-time","nullable":true},"carrier":{"type":"string","nullable":true}}},"InvoiceTaxLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceId":{"type":"string","format":"uuid"},"taxType":{"type":"string"},"authority":{"type":"string","nullable":true},"productRef":{"type":"string","nullable":true},"rate":{"type":"number","nullable":true},"rateDisplay":{"type":"string","nullable":true},"gallons":{"type":"integer","nullable":true},"amount":{"type":"number"}}},"CreateInvoiceRequest":{"type":"object","required":["subtotal","total","lineItems"],"properties":{"orderId":{"type":"string","format":"uuid","nullable":true},"deliveryStopId":{"type":"string","format":"uuid","nullable":true,"description":"Split load only: the delivery stop this invoice bills, so one multi-drop load can be billed once per stop. Requires orderId, and must be a stop of THAT order and not SKIPPED — a stop id sent without an orderId is rejected (400) rather than ignored. Omit to bill the order as a whole. An order is invoiced either as a whole or per stop, never both."},"payerCompanyId":{"type":"string","format":"uuid","nullable":true},"invoiceNumber":{"type":"string","minLength":1,"maxLength":50,"description":"Optional. Omit it and the server assigns your company's next number, INV-<year>-<sequence> (e.g. INV-2026-00042). A number you send is kept as given and must be unique for your company (409 otherwise)."},"subtotal":{"type":"number","minimum":0},"tax":{"type":"number","minimum":0,"default":0},"total":{"type":"number","minimum":0},"fuelCreditAmount":{"type":"number","nullable":true,"description":"Extraction fuel credit reported by the carrier for gallons pumped out. Displayed as its own line; deliberately NOT included in subtotal, tax, or total."},"dueDate":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","maxLength":2000,"nullable":true},"contractNumber":{"type":"string","maxLength":64,"nullable":true},"poNumber":{"type":"string","maxLength":64,"nullable":true},"lineItems":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","required":["description"],"properties":{"description":{"type":"string","maxLength":200},"gallons":{"type":"integer","nullable":true},"grossGallons":{"type":"integer","nullable":true},"netGallons":{"type":"integer","nullable":true},"rate":{"type":"number","nullable":true},"amount":{"type":"number","minimum":0,"nullable":true,"description":"Line total. Required unless rate and gallons are both present (then computed as rate × gallons). If supplied with rate and gallons it must match rate × gallons within a small rounding tolerance, else 400."},"product":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"spec":{"type":"string","nullable":true},"bolNumber":{"type":"string","nullable":true},"deliveryTicket":{"type":"string","nullable":true},"deliveredDate":{"type":"string","format":"date-time","nullable":true},"carrier":{"type":"string","nullable":true}}}},"taxLines":{"type":"array","maxItems":50,"items":{"type":"object","required":["taxType","amount"],"properties":{"taxType":{"type":"string","maxLength":100},"authority":{"type":"string","nullable":true},"rate":{"type":"number","nullable":true},"gallons":{"type":"integer","nullable":true},"amount":{"type":"number","minimum":0}}}}}},"MarkPaidRequest":{"type":"object","properties":{"paidAt":{"type":"string","format":"date-time"},"notes":{"type":"string","maxLength":1000,"nullable":true}}},"RackPrice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"terminalId":{"type":"string"},"product":{"type":"string"},"price":{"type":"number"},"unit":{"type":"string","enum":["gal","liter","bbl","mt"]},"spread":{"type":"number"},"indexSource":{"type":"string","nullable":true},"effectiveAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BulkRackPriceRequest":{"type":"object","required":["prices"],"properties":{"prices":{"type":"array","minItems":1,"maxItems":2000,"items":{"type":"object","required":["terminalId","product","price"],"properties":{"terminalId":{"type":"string","maxLength":50},"product":{"type":"string","maxLength":100},"price":{"type":"number","exclusiveMinimum":0,"maximum":999999},"unit":{"type":"string","enum":["gal","liter","ltr","bbl","mt"],"default":"gal"},"brand":{"type":"string","enum":["BRANDED","UNBRANDED","UNKNOWN"],"description":"Branded vs unbranded; omitted = UNKNOWN. Part of the price's identity: a BRANDED line moves the branded price."},"supplierBrand":{"type":"string","maxLength":120,"description":"The posting supplier when it is not you (e.g. an OPIS sheet). Part of the identity."},"terms":{"type":"string","maxLength":30,"nullable":true},"effectiveAt":{"type":"string","format":"date-time","description":"Omitted = now. A future time schedules the price; the current one stays live until then."},"expiresAt":{"type":"string","format":"date-time","nullable":true,"description":"End of the window; null clears; omitted keeps the stored value."},"unitCode":{"type":"string","enum":["gal","liter","ltr","bbl","mt"],"description":"Alias for `unit`."}}}},"source":{"type":"string","enum":["manual","argus","platts","opis"],"default":"manual"},"published":{"type":"boolean","description":"Post these prices to your connected accounts. Omitted: a new posting is private and an updated one keeps its current flag."}}},"FreightCalculateRequest":{"type":"object","required":["originTerminalId","destinationZip","product"],"properties":{"originTerminalId":{"type":"string","maxLength":100},"destinationZip":{"type":"string","minLength":3,"maxLength":20},"product":{"type":"string","maxLength":100},"gallons":{"type":"number","minimum":1,"multipleOf":0.001,"description":"Required unless `quantity` is sent instead. `quantity` wins if both are present."},"quantity":{"type":"number","minimum":1,"multipleOf":0.001,"description":"Alias for `gallons`. Required unless `gallons` is sent instead; wins if both are present."},"ratePerMile":{"type":"number","minimum":0},"distanceMiles":{"type":"number","minimum":0},"fuelSurchargePercent":{"type":"number","minimum":0,"maximum":100,"default":0},"additionalFees":{"type":"number","minimum":0,"default":0},"connectionId":{"type":"string","format":"uuid","description":"When provided, resolves a saved freight rate for this connection + lane."},"originCity":{"type":"string","maxLength":120},"originState":{"type":"string","maxLength":2}}},"FreightQuote":{"type":"object","properties":{"totalFreight":{"type":"number"},"distanceMiles":{"type":"number"},"ratePerMile":{"type":"number"},"fuelSurcharge":{"type":"number"},"additionalFees":{"type":"number"},"baseCost":{"type":"number"}}},"Connection":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"fromCompanyId":{"type":"string","format":"uuid"},"toCompanyId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","INFO_REQUESTED","RESUBMITTED","ACCEPTED","DECLINED"]},"message":{"type":"string","nullable":true},"erpId":{"type":"string","nullable":true},"creditLimit":{"type":"number","nullable":true},"paymentTerms":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"ConnectionFreightRate":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"connectionId":{"type":"string","format":"uuid"},"originTerminalId":{"type":"string","format":"uuid","nullable":true},"originCity":{"type":"string","nullable":true},"originState":{"type":"string","nullable":true},"destinationZipPrefix":{"type":"string","nullable":true},"destinationZip":{"type":"string","nullable":true},"destinationCity":{"type":"string","nullable":true},"destinationState":{"type":"string","nullable":true},"product":{"type":"string","nullable":true},"ratePerMile":{"type":"number","nullable":true},"ratePerGallon":{"type":"number","nullable":true},"minBillableQuantity":{"type":"number","nullable":true},"flatFee":{"type":"number","nullable":true},"minCharge":{"type":"number","nullable":true},"fuelSurchargePercent":{"type":"number","nullable":true},"distanceUnit":{"type":"string","enum":["MILES","KILOMETERS"],"default":"MILES"},"tierBasis":{"type":"string","enum":["GALLONS","MILES"],"default":"GALLONS"},"effectiveAt":{"type":"string","format":"date-time"},"notes":{"type":"string","nullable":true},"customFees":{"type":"array","nullable":true,"items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"amount":{"type":"number","nullable":true},"unit":{"type":"string","nullable":true}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateFreightRateRequest":{"type":"object","required":["connectionId"],"description":"At least one pricing field (ratePerMile, flatFee, minCharge, fuelSurchargePercent, or customFees) is required.","properties":{"connectionId":{"type":"string","format":"uuid"},"originTerminalId":{"type":"string","format":"uuid","nullable":true},"originCity":{"type":"string","maxLength":120,"nullable":true},"originState":{"type":"string","maxLength":2,"nullable":true},"destinationZipPrefix":{"type":"string","pattern":"^\\d{3}$","nullable":true},"destinationZip":{"type":"string","pattern":"^\\d{5}$","nullable":true},"destinationCity":{"type":"string","maxLength":120,"nullable":true},"destinationState":{"type":"string","maxLength":2,"nullable":true},"product":{"type":"string","maxLength":100,"nullable":true},"ratePerMile":{"type":"number","minimum":0,"nullable":true},"ratePerGallon":{"type":"number","minimum":0,"maximum":100,"nullable":true,"description":"EC-3480 — cents-per-gallon linehaul. Additive with ratePerMile, not exclusive."},"minBillableQuantity":{"type":"number","minimum":0,"nullable":true,"description":"EC-3483 — bill at least this quantity. Floors the QUANTITY the rate applies to, unlike minCharge which floors the dollars."},"flatFee":{"type":"number","minimum":0,"nullable":true},"minCharge":{"type":"number","minimum":0,"nullable":true},"fuelSurchargePercent":{"type":"number","minimum":0,"maximum":100,"nullable":true},"distanceUnit":{"type":"string","enum":["MILES","KILOMETERS"],"default":"MILES"},"tierBasis":{"type":"string","enum":["GALLONS","MILES"],"default":"GALLONS","description":"EC-3480 — what `tiers` bands on. GALLONS is the default and the pre-EC-3480 behaviour."},"effectiveAt":{"type":"string","format":"date-time"},"notes":{"type":"string","maxLength":1000,"nullable":true},"customFees":{"type":"array","maxItems":20,"nullable":true,"items":{"type":"object","required":["label"],"properties":{"label":{"type":"string","maxLength":100},"amount":{"type":"number","minimum":0,"nullable":true},"unit":{"type":"string","maxLength":20,"nullable":true}}}}}},"UpdateFreightRateRequest":{"type":"object","description":"Partial update — all fields optional. connectionId is immutable.","properties":{"originTerminalId":{"type":"string","format":"uuid","nullable":true},"originCity":{"type":"string","maxLength":120,"nullable":true},"originState":{"type":"string","maxLength":2,"nullable":true},"destinationZipPrefix":{"type":"string","pattern":"^\\d{3}$","nullable":true},"destinationZip":{"type":"string","pattern":"^\\d{5}$","nullable":true},"product":{"type":"string","maxLength":100,"nullable":true},"ratePerMile":{"type":"number","minimum":0,"nullable":true},"ratePerGallon":{"type":"number","minimum":0,"maximum":100,"nullable":true,"description":"EC-3480 — cents-per-gallon linehaul. Additive with ratePerMile, not exclusive."},"minBillableQuantity":{"type":"number","minimum":0,"nullable":true,"description":"EC-3483 — bill at least this quantity. Floors the QUANTITY the rate applies to, unlike minCharge which floors the dollars."},"flatFee":{"type":"number","minimum":0,"nullable":true},"minCharge":{"type":"number","minimum":0,"nullable":true},"fuelSurchargePercent":{"type":"number","minimum":0,"maximum":100,"nullable":true},"tierBasis":{"type":"string","enum":["GALLONS","MILES"],"default":"GALLONS","description":"EC-3480 — what `tiers` bands on. GALLONS is the default and the pre-EC-3480 behaviour."},"effectiveAt":{"type":"string","format":"date-time"},"notes":{"type":"string","maxLength":1000,"nullable":true}}},"DeliveryTicket":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ticketNumber":{"type":"string"},"orderId":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["DRAFT","ISSUED","SIGNED","VOID"]},"supplierCompanyId":{"type":"string","format":"uuid","nullable":true},"supplierName":{"type":"string","nullable":true,"description":"Ad-hoc supplier name, when the supplier is not a company in our directory. Null when supplierCompany identifies a known company."},"supplierSetBy":{"type":"string","nullable":true,"enum":["POSTER","CARRIER","DRIVER","DISPATCHER"],"description":"Who reported the supplier. DISPATCHER means an integrated dispatch system sent it over the public API and the value was mirrored, not validated against our directory. Null on rows written before this field existed, and on tickets naming no supplier."},"receiverCompanyId":{"type":"string","format":"uuid","nullable":true},"bolNumber":{"type":"string","nullable":true,"description":"Every BOL on this ticket, comma-joined. Compat mirror of bolNumbers — read bolNumbers instead."},"bolNumbers":{"type":"array","items":{"type":"string"},"description":"All BOLs covered by this ticket (one BOL is issued per pickup stop). Empty when none recorded."},"deliveryDate":{"type":"string","format":"date-time","nullable":true},"driverName":{"type":"string","nullable":true},"carrierCompanyName":{"type":"string","nullable":true},"loadingTerminalName":{"type":"string","nullable":true},"destinationName":{"type":"string","nullable":true},"productLines":{"type":"array","nullable":true,"items":{"type":"object","properties":{"product_name":{"type":"string"},"spec":{"type":"string"},"gross_gallons":{"type":"number"},"net_gallons":{"type":"number"},"temperature":{"type":"number"},"api_gravity":{"type":"number"}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateDeliveryTicketRequest":{"type":"object","description":"All fields optional — tickets can be saved as DRAFT incrementally. ticketNumber is auto-generated when omitted.","properties":{"ticketNumber":{"type":"string","maxLength":64},"orderId":{"type":"string","format":"uuid"},"supplierCompanyId":{"type":"string","format":"uuid","description":"A supplier that IS a company in our directory. Must be you, a party on the referenced order, or a company you hold an ACCEPTED connection with — otherwise 403. For a supplier we have no company for, send supplierName instead."},"supplierName":{"type":"string","maxLength":200,"description":"Ad-hoc supplier, free text. Use when the supplier is not a company in our directory. Not connection-gated (it is not a reference to a company), and recorded with supplierSetBy so readers can see a dispatch system reported it. Send this OR supplierCompanyId, not both."},"receiverCompanyId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["DRAFT","ISSUED","VOID"],"description":"SIGNED is not settable here: only the receiver signs, through POST /delivery-tickets/{id}/sign."},"bolNumber":{"type":"string","maxLength":64,"description":"Single BOL. Legacy — prefer bolNumbers. When sent alone it is comma-split into bolNumbers."},"bolNumbers":{"type":"array","maxItems":50,"items":{"type":"string","maxLength":64},"description":"All BOLs on this ticket. Wins over bolNumber when both are sent."},"deliveryDate":{"type":"string","format":"date-time"},"driverName":{"type":"string","maxLength":120},"carrierCompanyName":{"type":"string","maxLength":200},"loadingTerminalName":{"type":"string","maxLength":200},"destinationName":{"type":"string","maxLength":200},"productLines":{"type":"array","maxItems":50,"items":{"type":"object","properties":{"product_name":{"type":"string","maxLength":120},"spec":{"type":"string","maxLength":120},"gross_gallons":{"type":"number"},"net_gallons":{"type":"number"},"temperature":{"type":"number"},"api_gravity":{"type":"number"}}}},"deliveryNotes":{"type":"string","maxLength":2000}}},"SignDeliveryTicketRequest":{"type":"object","required":["receiverName"],"properties":{"receiverName":{"type":"string","minLength":1,"maxLength":120},"discrepancies":{"type":"string","maxLength":2000}}},"BudgetSettings":{"type":"object","required":["thresholdPct"],"additionalProperties":false,"properties":{"thresholdPct":{"type":"integer","minimum":1,"maximum":100,"default":80,"description":"Alert when a site's invoiced month-to-date spend reaches this % of its monthly budget."},"orderApprovalThreshold":{"type":"number","minimum":0,"multipleOf":0.01,"nullable":true,"description":"EC-4785 dollars. An order request from a field user (VIEWER) needs a Manager-or-above approval when it is over this, has no confirmed price, or this is null (the default)."}}},"Location":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"name":{"type":"string"},"locationType":{"type":"string","nullable":true},"street":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","default":"US"},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"gateCode":{"type":"string","nullable":true},"monthlyBudget":{"type":"number","nullable":true,"description":"EC-4258 — monthly spend budget for this site, in dollars; null = no budget."},"isActive":{"type":"boolean"},"contacts":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"isPrimary":{"type":"boolean"}}}},"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Asset":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"name":{"type":"string"},"assetType":{"type":"string","enum":["TANK","GENERATOR","EQUIPMENT","VEHICLE","OTHER"]},"product":{"type":"string","nullable":true},"capacity":{"type":"integer"},"currentLevel":{"type":"integer"},"lastReadingAt":{"type":"string","format":"date-time","nullable":true,"description":"When currentLevel was last read (tank monitor or manual entry). Null when it has never been read."},"unit":{"type":"string","default":"gal"},"fillMethod":{"type":"string","nullable":true},"maxFillPct":{"type":"integer","minimum":0,"maximum":100,"default":90},"autoReorderPct":{"type":"integer","nullable":true}}},"SupplierTerminal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"terminalCode":{"type":"string"},"name":{"type":"string"},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"products":{"type":"object","nullable":true},"capacity":{"type":"integer","nullable":true},"operatingHours":{"type":"string","nullable":true},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Allocation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyId":{"type":"string","format":"uuid"},"connectionId":{"type":"string","format":"uuid"},"terminalId":{"type":"string"},"product":{"type":"string"},"dailyAllocation":{"type":"integer","nullable":true},"weeklyAllocation":{"type":"integer","nullable":true},"monthlyAllocation":{"type":"integer","nullable":true},"dailyUsed":{"type":"integer"},"weeklyUsed":{"type":"integer"},"monthlyUsed":{"type":"integer"},"period":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Payment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceId":{"type":"string","format":"uuid"},"amount":{"type":"number"},"method":{"type":"string"},"reference":{"type":"string","nullable":true},"stripeChargeId":{"type":"string","nullable":true},"status":{"type":"string","enum":["PENDING","PROCESSING","SUCCEEDED","FAILED","REFUNDED"]},"failureReason":{"type":"string","nullable":true},"surchargeAmount":{"type":"number","nullable":true},"paidAt":{"type":"string","format":"date-time"}}},"CompanyProfile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["SUPPLIER","BROKER","CARRIER","CUSTOMER"]},"dba":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"logoUrl":{"type":"string","nullable":true},"billingStreet":{"type":"string","nullable":true},"billingCity":{"type":"string","nullable":true},"billingState":{"type":"string","nullable":true},"billingZip":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}}},"parameters":{"pageParam":{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},"pageSizeParam":{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}},"responses":{"BadRequest":{"description":"Validation failed — body, query, or path params did not match the schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid credentials (Authorization header / X-API-Key / refresh cookie).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Authenticated but lacks the required role, scope, or IP allowlist match.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Throttled. `error.code` is `RATE_LIMIT_EXCEEDED` for both limits; `error.kind` tells them apart: `burst` (per-key minute limit) or `quota` (monthly cap, resets `resetAt`). RFC 9239 headers (`RateLimit-*`, `Retry-After`) are always present; a `quota` 429 also carries `X-Quota-Remaining: 0`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed in the current window"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the window"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets"},"RateLimit-Policy":{"schema":{"type":"string"},"description":"e.g. `600;w=60`"},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds the caller should wait before retrying"},"X-Quota-Remaining":{"schema":{"type":"integer"},"description":"Monthly requests left for this key (`0` on a `quota` 429)"},"X-Quota-Reset":{"schema":{"type":"integer"},"description":"Unix epoch seconds when the monthly quota resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}}}},"security":[{"apiKeyAuth":[]}],"tags":[{"name":"Auth","description":"Authentication, registration, MFA, sessions"},{"name":"Companies","description":"Company profiles, team management, documents, operational data"},{"name":"Connections","description":"Company-to-company connection workflow (10-state machine)"},{"name":"Orders","description":"Order lifecycle, bids, pickup, delivery, verification"},{"name":"Load Board","description":"Carrier-facing: browse loads, submit bids, manage hauls"},{"name":"Comms","description":"Messaging threads (chat/email/SMS/voice), folders, templates"},{"name":"Pricing","description":"Rack pricing, customer-specific pricing, auto-update rules, OCR"},{"name":"Invoices","description":"Invoice lifecycle, payments, PDF generation, bulk operations"},{"name":"Notifications","description":"In-app notifications, preferences, push subscriptions"},{"name":"Analytics","description":"Dashboard KPIs, revenue/volume time series, carrier scoring"},{"name":"Locations","description":"Customer locations, assets (tanks), contacts, schedules"},{"name":"Allocations","description":"Terminal product allocations, request more capacity"},{"name":"RFPs","description":"Requests for proposal: sealed bids, award to contract (EC-2893)"},{"name":"Webhooks","description":"Outbound event subscriptions: HMAC-signed deliveries, retries, replay (EC-2147)"},{"name":"Templates","description":"Email/SMS/Chat message templates per company"},{"name":"Integrations","description":"Email OAuth, tank monitors, ELD, third-party connections"},{"name":"Files","description":"GCS signed upload URLs, file metadata, downloads"},{"name":"GDPR","description":"Data subject rights — erasure, portability, admin workflow"},{"name":"Discovery","description":"Company search and discovery for prospect map"},{"name":"Reviews","description":"Post-delivery carrier/broker reviews"},{"name":"Markets","description":"Saved terminal groups for rack pricing views"},{"name":"API Keys","description":"Company API key management"},{"name":"Support","description":"Support tickets and messages"},{"name":"Billing","description":"Payment methods, billing settings"},{"name":"Accounting","description":"QuickBooks/Xero/Sage integration via unified API"},{"name":"Geocoding","description":"Address autocomplete, details, reverse geocoding"},{"name":"Addresses","description":"Saved addresses (Places/geocode), company-scoped cache"},{"name":"Supplier Terminals","description":"Terminal registry for supplier companies"},{"name":"Product Taxonomy","description":"Canonical EC product IDs, alias matrix, hazmat, terminal packages, brand registry"},{"name":"Marketing","description":"Public marketing site lead capture (demo requests)"},{"name":"Mobile","description":"Carrier mobile app support endpoints (launch gate)"},{"name":"Org","description":"Org families: HQ/subsidiary links, command center, rollups (EC-1305..)"},{"name":"Service Orders","description":"Service requests: post, quote, award, complete (sealed bids, $20 fee)"},{"name":"Capacity Exchange","description":"Capacity listings + sealed bids; approval mints an Order (EC-351)"},{"name":"Atom","description":"ATOM assistant widget: gate, chat, feedback"},{"name":"Load Alerts","description":"Geo-fence load alert regions (EC-900)"},{"name":"Broker Customers","description":"Broker customer-book bulk import (EC-387)"},{"name":"Carrier Customers","description":"Carrier customer-book bulk import (EC-385)"},{"name":"Contracts","description":"LLM contract/document scan (EC-702)"},{"name":"Driver Pay","description":"Per-load driver pay for first-party carriers"},{"name":"Minigames","description":"Server-authoritative minigame runs — Flying ATOM run tokens, replay-validated completion, best score (EC-3082)"}],"webhooks":{"ec-event":{"post":{"tags":["Webhooks"],"summary":"Delivery to the url of your subscription (we call you)","description":"Energy Connect POSTs this body to the url of your webhook subscription. Answer 2xx in 10 seconds. Any other answer starts the retry schedule (1 min, 5 min, 30 min, 2 h, 12 h) and then dead-letters the delivery. Read the dead letters with GET /webhooks/{id}/deliveries.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventEnvelope"}}}},"responses":{"200":{"description":"Your endpoint accepted the event."}}}}},"paths":{"/orders":{"get":{"tags":["Orders"],"summary":"List orders (paginated, filterable)","parameters":[{"name":"role","in":"query","description":"Public API (/v1/public/orders) only. `originator` (default) returns orders your company posted; `carrier` returns hauls assigned to your company — the only view that returns rows for a carrier that never posts orders. Carrier rows are masked for disintermediation until the haul is ACCEPTED: while it is still an unaccepted offer you get destination city and state only (street, ZIP, lat/lng, destinationName and location.name are null). Acceptance releases the full record. Carrier reads ignore product/sortBy/sortOrder. `broker` (EC-4794) returns loads a supplier posted directly to your company as its broker, at every status unless `status` narrows it, masked the same way as the broker portal: location.name and location.companyId are always null, destinationName stays null until you accept (unless the poster marks you a trusted partner), and lines carry the freight rate but never the poster's buy/sell prices. `bucket` is role=carrier only.","schema":{"type":"string","enum":["originator","carrier","broker"],"default":"originator"}},{"name":"status","in":"query","description":"Filter by order status; comma-separated values accepted. The literal value `all` returns every readable status — needed on role=carrier, where an OMITTED filter returns only ACCEPTED hauls (CONFIRMED, IN_TRANSIT, DELIVERED, PENDING_VERIFICATION, INVOICED) rather than everything. Note that accepting a load moves it OFF status ASSIGNED, so ASSIGNED is a feed of pending offers, not of dispatchable work — use bucket=dispatch_ready for that.","schema":{"type":"string","enum":["all","DRAFT","POSTED","BIDDING","ASSIGNED","CONFIRMED","IN_TRANSIT","DELIVERED","PENDING_VERIFICATION","INVOICED","CANCELLED","PETITIONED","PETITION_DECLINED"]}},{"name":"bucket","in":"query","description":"Public API, role=carrier only (400 otherwise, and 400 if combined with `status`). Filters by lifecycle bucket rather than raw status, because the states an automation acts on are not expressible as a status alone — they combine status with dispatch progress. `dispatch_ready` = accepted and no driver sent yet, i.e. the loads you can dispatch right now. `assigned` = the carrier portal’s Assigned tab: unaccepted offers PLUS accepted-not-yet-dispatched. Offers surfaced through a bucket stay address-masked until accepted.","schema":{"type":"string","enum":["dispatch_ready","assigned","loading","loadcomplete","delivering","complete","invoiced","petition","petition_declined"]}},{"name":"product","in":"query","description":"Filter by product name","schema":{"type":"string","maxLength":50}},{"name":"search","in":"query","description":"Free-text search across order fields","schema":{"type":"string","maxLength":100}},{"name":"dateFrom","in":"query","description":"Inclusive lower bound (ISO 8601 date-time) on the order’s effective delivery date — the reported delivery when one exists, else the scheduled delivery date, else the requested date, else creation time. Works on both roles. Pair with dateTo to page one month at a time instead of walking the full history.","schema":{"type":"string","format":"date-time"}},{"name":"dateTo","in":"query","description":"EXCLUSIVE upper bound (ISO 8601 date-time) on the effective delivery date — send the first instant of the next window (e.g. the 1st of the next month).","schema":{"type":"string","format":"date-time"}},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["createdAt","requestedDate","status","updatedAt"],"default":"createdAt"}},{"name":"sortOrder","in":"query","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}},{"name":"updatedSince","in":"query","description":"Public API only. Return rows whose updatedAt is at or after this ISO 8601 date-time, ordered by updatedAt then id (ascending). sortBy, sortOrder and page are ignored, and meta becomes { pageSize, hasMore, nextCursor }. Walk by passing meta.nextCursor back as `cursor` until hasMore is false; an unchanged row is never skipped or repeated, and a row changed during the walk comes back again later with its new state. Keep the last nextCursor to resume the next sync run. Rows changed in the last 60 seconds are held back until a later page or run, so a change still being committed is never passed over. A walk reports changes only: a row that is deleted or discarded, or that leaves the filter (cancelled, unassigned, or moved to a status you did not ask for), simply stops appearing, so send status=all on order syncs and reconcile missing rows by id. Viewing or re-sending an invoice also moves its updatedAt, so expect harmless repeats.","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"Public API only. The meta.nextCursor of the previous page of an updatedSince walk. Opaque; requires updatedSince (400 otherwise, and 400 for a malformed cursor).","schema":{"type":"string","maxLength":200}},{"$ref":"#/components/parameters/pageParam"},{"$ref":"#/components/parameters/pageSizeParam"}],"responses":{"200":{"description":"Paginated order list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"},"totalPages":{"type":"integer"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true,"description":"Public API, updatedSince walks only: pass as `cursor` (with the same updatedSince) for the next page. Points at the last row returned (never at a row changed in the last 60 seconds); store it to resume the next sync run from there."}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Orders"],"summary":"Create new order","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"201":{"description":"Order created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/orders/{id}":{"get":{"tags":["Orders"],"summary":"Get order details","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"role","in":"query","description":"Public API (/v1/public/orders/{id}) only. Must match the `role` used to list the order — `carrier` resolves a haul assigned to your company, which the default `originator` view returns 404 for; `broker` resolves a load posted to your company as its broker.","schema":{"type":"string","enum":["originator","carrier","broker"],"default":"originator"}}],"responses":{"200":{"description":"Order with lines, bids, pickup, delivery","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Orders"],"summary":"Update order","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrderRequest"}}}},"responses":{"200":{"description":"Updated order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/cancel":{"post":{"tags":["Orders"],"summary":"Cancel an order (own orders only; not after delivery)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Order cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/notes":{"get":{"tags":["Orders"],"summary":"List an order's notes (chronological)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Notes in chronological order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicOrderNote"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Orders"],"summary":"Append a note to an order (any status)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","minLength":1,"maxLength":2000}}}}}},"responses":{"201":{"description":"Note created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/PublicOrderNote"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/documents":{"post":{"tags":["Orders"],"summary":"Attach a document to an order (step 2 — register by fileUploadId)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["docType","fileUploadId"],"properties":{"docType":{"type":"string","maxLength":50,"description":"e.g. order_bol, haul_pod, order_doc"},"fileUploadId":{"type":"string","format":"uuid","description":"From POST /uploads"}}}}}},"responses":{"201":{"description":"Attached document","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/OrderDocument"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"get":{"tags":["Orders"],"summary":"List an order's documents (BOL, POD, tickets)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Documents, newest first. fileUrl is a short-lived signed URL; persist downloadUrl instead.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/OrderDocument"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/uploads":{"post":{"tags":["Orders"],"summary":"Request a signed upload URL (step 1 of attaching a document)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fileName","mimeType","fileSize","context"],"properties":{"fileName":{"type":"string","maxLength":255},"mimeType":{"type":"string","description":"One of the allowed document/image types (application/pdf, image/jpeg, …)"},"fileSize":{"type":"integer","description":"Bytes; max 50MB"},"context":{"type":"string","enum":["order_doc","order_bol","order_pod","haul_bol","haul_pod","invoice_doc"],"description":"The required scope follows the context: invoice_doc rides invoices:write, all others ride orders:write"}}}}}},"responses":{"201":{"description":"Signed PUT URL + fileUploadId. PUT the raw file bytes to uploadUrl, then register with POST /orders/{id}/documents.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"fileUploadId for the register step"},"uploadUrl":{"type":"string","description":"Signed PUT target — expires in expiresIn seconds"},"fileUrl":{"type":"string"},"expiresIn":{"type":"integer"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/orders/{id}/documents/{docId}/content":{"get":{"tags":["Orders"],"summary":"Stable document download link (302 to a freshly signed URL)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"docId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"302":{"description":"Redirect to the file content (follow it immediately; the target URL expires)"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoice-amendments":{"get":{"tags":["Invoices"],"summary":"List invoice amendments across your invoices (issuer or payer side)","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","APPROVED","DECLINED","WITHDRAWN"]}},{"name":"invoiceId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Amendments, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceAmendment"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Invoices"],"summary":"Propose an amendment to an issued invoice (issuer only; one PENDING per invoice)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["invoiceId","proposedChanges"],"properties":{"invoiceId":{"type":"string","format":"uuid"},"proposedChanges":{"type":"object","description":"Allowlisted invoice fields (subtotal/tax/total are re-derived server-side on approve); lineItems, when present, is a FULL replacement of the current list"},"proposeNotes":{"type":"string","maxLength":2000},"expectedBase":{"type":"object","required":["total","lineIds"],"additionalProperties":false,"description":"Optional (EC-3949): the invoice total and line item ids your change was built from. If the invoice no longer matches, the request fails 409 AMENDMENT_BASE_STALE and nothing is recorded — re-read the invoice and rebuild the change","properties":{"total":{"type":"number"},"lineIds":{"type":"array","maxItems":1000,"items":{"type":"string","format":"uuid"}}}}}}}}},"responses":{"201":{"description":"Created amendment (PENDING)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/InvoiceAmendment"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the invoice issuer can propose an amendment"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"An amendment is already pending for this invoice (error.code CONFLICT), or expectedBase no longer matches the invoice (error.code AMENDMENT_BASE_STALE; not retryable as-is)"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/invoice-amendments/{id}":{"get":{"tags":["Invoices"],"summary":"Get an invoice amendment (issuer or payer)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Amendment","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/InvoiceAmendment"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Not found — also returned to a company that is neither issuer nor payer (EC-4381)"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/documents/{docId}":{"delete":{"tags":["Orders"],"summary":"Delete a document (soft; owner or assigned carrier, within 36h of completion)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"docId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not a participant on the order, or the 36h post-completion edit window has closed"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoices/{id}/documents":{"post":{"tags":["Invoices"],"summary":"Attach supporting documents to an invoice (issuer only; upload with context invoice_doc first)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fileUploadIds"],"properties":{"fileUploadIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":20,"description":"From POST /uploads with context invoice_doc"}}}}}},"responses":{"200":{"description":"Updated invoice with documents[]","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the invoice issuer can attach documents"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoices/{id}/documents/{fileId}":{"delete":{"tags":["Invoices"],"summary":"Remove a supporting document (issuer only; never the primary external upload; blocked while DISPUTED)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"fileId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Updated invoice with documents[]","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"400":{"description":"Invoice is DISPUTED — documents are frozen as evidence"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the invoice issuer can remove documents"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhook subscriptions","responses":{"200":{"description":"Subscriptions (no secrets)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Webhooks"],"summary":"Create a webhook subscription (response carries the signing secret ONCE)","description":"The body we deliver to your url is WebhookEventEnvelope. It carries identifiers only, never the entity. Fetch the entity through the public API.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","description":"https on a publicly routable host"},"events":{"type":"array","items":{"type":"string","enum":["order.updated","order.accepted","bid.created","bid.accepted","bid.declined","bid.countered","bid.withdrawn","invoice.issued","invoice.paid","delivery_ticket.signed","bol.submitted","delivery.rejected","invoice.created","invoice.voided","invoice.disputed","connection.updated","terminal_access.updated","pricing.updated"]},"minItems":1,"description":"The event types to deliver. See WebhookEventEnvelope for the body of each type."},"description":{"type":"string","maxLength":500}}}}}},"responses":{"201":{"description":"Created subscription incl. secret (shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/webhooks/{id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook subscription","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscription (no secret)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"patch":{"tags":["Webhooks"],"summary":"Update url/events/description/isActive","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"description":{"type":"string","nullable":true},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated subscription","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook subscription","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/webhooks/{id}/rotate-secret":{"post":{"tags":["Webhooks"],"summary":"Rotate the signing secret (response carries the new secret ONCE)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Subscription incl. new secret (shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"Delivery history incl. dead-letters","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","SUCCEEDED","DEAD"]}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Deliveries, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookOutboundDeliveryRow"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/webhooks/{id}/replay":{"post":{"tags":["Webhooks"],"summary":"Re-enqueue events since a timestamp (catch-up; same stable event ids)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["since"],"properties":{"since":{"type":"string","format":"date-time","description":"At most 7 days ago; at most 1000 events re-enqueued"}}}}}},"responses":{"200":{"description":"Count of re-enqueued events","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"enqueued":{"type":"integer"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoice-amendments/{id}/approve":{"post":{"tags":["Invoices"],"summary":"Approve a pending amendment (payer only) — applies the changes atomically","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"responseNotes":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Approved amendment; the invoice now reflects the proposed changes (totals re-derived server-side)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/InvoiceAmendment"}}}}}},"400":{"description":"Amendment already responded to, or the invoice is PAID/VOID"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the payer can approve an amendment"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"A concurrent response already claimed this amendment (error.code CONFLICT; re-read it), or the invoice changed after this amendment was proposed so it can no longer be approved (error.code AMENDMENT_BASE_CHANGED; not retryable)"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/publish":{"post":{"tags":["Orders"],"summary":"Publish order (DRAFT → POSTED) — sends a carrier-addressed order as an offer, otherwise posts to the load board","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Published order","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/bids":{"post":{"tags":["Load Board"],"summary":"Submit bid on an order","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"productPricing":{"type":"object","nullable":true,"description":"Formula pricing per order line (basis + signed adder). Optional — omit for a freight-only bid.","properties":{"lines":{"type":"array","minItems":1,"maxItems":30,"items":{"type":"object","required":["lineId","basis","pricingBasis","adderCents"],"properties":{"lineId":{"type":"string","format":"uuid"},"basis":{"type":"string","enum":["opis","argus","platts","nycse","dtn","terminal_posted"]},"pricingBasis":{"type":"string","enum":["GROSS","NET"]},"adderCents":{"type":"integer","minimum":-10000,"maximum":10000,"description":"Signed cents added to the resolved basis price per unit. With `adderTiers`, this is the fallback used when no band claims the volume."},"terminalId":{"type":"string","maxLength":50,"description":"EC-2894 — the bidder's own rack point, when it differs from the order line's terminal. Same free-text terminal key as the order line carries (NOT a master-terminal uuid). Optional; omit to price off the line's terminal."},"adderTiers":{"type":"array","maxItems":20,"description":"EC-2896 — volume bands on the differential. `from` inclusive, `to` exclusive (null = open-ended top band). Bands must not overlap. Omit for a flat adder at any volume.","items":{"type":"object","required":["from","to","adderCents"],"properties":{"from":{"type":"number","minimum":0,"description":"Lower bound in the line unit, inclusive."},"to":{"type":"number","minimum":0,"nullable":true,"description":"Upper bound, EXCLUSIVE. Null for the open-ended top band."},"adderCents":{"type":"integer","minimum":-10000,"maximum":10000,"description":"Signed cents applied when the volume falls in this band."}}}},"rackBasis":{"type":"object","required":["kind"],"description":"EC-4616 — which rack row the basis reads: UNBRANDED_LOW (cheapest unbranded supplier; the default when omitted), RACK_AVERAGE, or SUPPLIER with supplierBrand (and optional brand).","properties":{"kind":{"type":"string","enum":["UNBRANDED_LOW","RACK_AVERAGE","SUPPLIER"]},"supplierBrand":{"type":"string","minLength":1,"maxLength":100},"brand":{"type":"string","enum":["BRANDED","UNBRANDED"]}}}}}}}}}}}}},"responses":{"201":{"description":"Bid created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"productPricing":{"type":"object","nullable":true,"description":"Echoes the submitted formula lines, or null for a freight-only bid."}},"additionalProperties":true}}}}}}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"get":{"tags":["Orders"],"summary":"GET /orders/{id}/bids","operationId":"get-orders-id-bids","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:297`. Middleware: authenticate, apiLimiter, tenantScope, requireRole.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/{id}/delivery":{"post":{"tags":["Orders"],"summary":"Submit delivery data (meter, stick, receiver); on a load-complete order this performs the transit hop itself. SPLIT LOAD (EC-2577): send `deliveryStopId` (plus `allocations[]` rows of {allocationId, deliveredQuantity, deliveredGrossQuantity?}, or `skipped`+`skipReason`) to resolve ONE stop — call once per stop, ids from the order detail read; the last stop completes the order. A whole-order body (no deliveryStopId) is rejected on a split load. A line added at the rack (`lines[].addedAtDelivery: true`) has NO allocation row and therefore no allocationId — report it in `addedLines[]` rows of {lineId, product, unitCode, deliveredQuantity, deliveredGrossQuantity?}, where `lineId` is that line's `lines[].id`. Omitting `lineId` CREATES a second added line instead of recording the delivery of the existing one. `addedLines[]` alone may resolve a stop. PUMP-OUT (EC-3717): send `isExtraction: true` when the job turned out to pull product OUT of the tank — gallons stay positive magnitudes and the order-level `isExtraction` flag carries the direction. Rejected on a split load, which records a pump-out as a negative stop allocation instead.","responses":{"201":{"description":"Delivery submitted"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]},"patch":{"tags":["Orders"],"summary":"Edit a submitted delivery within the 36h post-completion window. Accepts `isExtraction` (EC-3717) to mark — or, sent as false, to un-mark — the order as a pump-out; this is the only surface that can clear the flag, since order edits close at IN_TRANSIT. Rejected on a split load.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Delivery updated — full order returned"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The 36h edit window has closed"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/verify":{"post":{"tags":["Orders"],"summary":"Verify (approve) or reject a submitted delivery (own orders only). Approve moves the order to INVOICED; reject sends it back to the carrier to correct and requires notes as the reason.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["approved"],"properties":{"approved":{"type":"boolean","description":"true approves the delivery; false rejects it"},"notes":{"type":"string","maxLength":2000,"nullable":true,"description":"Required when approved is false: the reason the carrier sees"}}}}}},"responses":{"200":{"description":"The order after verification","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"description":"Validation error, or the order is not awaiting verification"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/load-board":{"get":{"tags":["Load Board"],"summary":"The carrier load board — open loads, disintermediation-masked (no end-customer identity; hasBids boolean, never a count)","parameters":[{"name":"product","in":"query","schema":{"type":"string"}},{"name":"urgency","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated load board with enriched data"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/bids/{bidId}/decline":{"put":{"tags":["Load Board"],"summary":"Decline a bid (with optional reason + carrier notification)","responses":{"200":{"description":"Bid declined"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/pricing/rack":{"get":{"tags":["Pricing"],"summary":"Get rack pricing","parameters":[{"name":"product","in":"query","description":"Filter by product name","schema":{"type":"string","maxLength":50}},{"name":"terminalId","in":"query","description":"Filter by terminal ID","schema":{"type":"string","maxLength":50}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":50}}],"responses":{"200":{"description":"Paginated rack price list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/RackPrice"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/customers":{"post":{"tags":["Customers"],"summary":"Create a customer in the caller's network","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartyRequest"}}}},"responses":{"201":{"description":"Customer created (Company + accepted Connection)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/NetworkParty"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/suppliers":{"post":{"tags":["Suppliers"],"summary":"Create a supplier in the caller's network","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartyRequest"}}}},"responses":{"201":{"description":"Supplier created (Company + accepted Connection)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/NetworkParty"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/pricing/bulk/rack":{"post":{"tags":["Pricing"],"summary":"Bulk upsert rack prices — each line updates the latest posting for its terminal × product × brand or adds a new posting (later different price, or a future effectiveAt), the same rule as PUT /pricing/master. One transaction per batch; MANAGER+","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkRackPriceRequest"}}}},"responses":{"200":{"description":"Update count","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"created":{"type":"integer","description":"Postings inserted"},"updated":{"type":"integer","description":"Postings edited in place"},"total":{"type":"integer"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/invoices":{"get":{"tags":["Invoices"],"summary":"List invoices (filterable by status)","parameters":[{"name":"status","in":"query","description":"Filter by invoice status","schema":{"type":"string","enum":["DRAFT","OUTSTANDING","PAID","OVERDUE","DISPUTED","VOID"]}},{"name":"role","in":"query","description":"View as issuer or payer","schema":{"type":"string","enum":["issuer","payer"],"default":"issuer"}},{"name":"orderId","in":"query","description":"Filter by linked order","schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","description":"Case-insensitive match on invoice number, notes, PO number, contract number, issuer or payer company name, or line-item description/product","schema":{"type":"string","maxLength":100}},{"name":"kind","in":"query","description":"Which invoices to list: 'order' (default — invoices not tied to a pricing subscription, the pre-subscription behaviour), 'subscription' (pricing-subscription invoices only) or 'all'","schema":{"type":"string","enum":["order","subscription","all"],"default":"order"}},{"name":"issuedFrom","in":"query","description":"Inclusive lower bound (ISO 8601 date-time) on issuedAt, when the invoice left DRAFT. Unissued drafts have no issuedAt, so any issued window leaves them out.","schema":{"type":"string","format":"date-time"}},{"name":"issuedTo","in":"query","description":"EXCLUSIVE upper bound (ISO 8601 date-time) on issuedAt — send the first instant of the next window.","schema":{"type":"string","format":"date-time"}},{"name":"updatedSince","in":"query","description":"Public API only. Return rows whose updatedAt is at or after this ISO 8601 date-time, ordered by updatedAt then id (ascending). sortBy, sortOrder and page are ignored, and meta becomes { pageSize, hasMore, nextCursor }. Walk by passing meta.nextCursor back as `cursor` until hasMore is false; an unchanged row is never skipped or repeated, and a row changed during the walk comes back again later with its new state. Keep the last nextCursor to resume the next sync run. Rows changed in the last 60 seconds are held back until a later page or run, so a change still being committed is never passed over. A walk reports changes only: a row that is deleted or discarded, or that leaves the filter (cancelled, unassigned, or moved to a status you did not ask for), simply stops appearing, so send status=all on order syncs and reconcile missing rows by id. Viewing or re-sending an invoice also moves its updatedAt, so expect harmless repeats.","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"Public API only. The meta.nextCursor of the previous page of an updatedSince walk. Opaque; requires updatedSince (400 otherwise, and 400 for a malformed cursor).","schema":{"type":"string","maxLength":200}},{"name":"dueFrom","in":"query","description":"Only invoices due at or after this instant","schema":{"type":"string","format":"date-time"}},{"name":"dueTo","in":"query","description":"Only invoices due before this instant (exclusive)","schema":{"type":"string","format":"date-time"}},{"name":"counterpartyCompanyId","in":"query","description":"The other party: the payer when role=issuer, the issuer when role=payer. Narrows the caller's own invoices only","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/pageParam"},{"$ref":"#/components/parameters/pageSizeParam"}],"responses":{"200":{"description":"Paginated invoice list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"},"totalPages":{"type":"integer"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true,"description":"Public API, updatedSince walks only: pass as `cursor` (with the same updatedSince) for the next page. Points at the last row returned (never at a row changed in the last 60 seconds); store it to resume the next sync run from there."}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Invoices"],"summary":"Create invoice","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvoiceRequest"}}}},"responses":{"201":{"description":"Invoice created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/invoices/{id}/pdf":{"get":{"tags":["Invoices"],"summary":"Download invoice PDF","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"PDF binary","content":{"application/pdf":{}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/locations":{"get":{"tags":["Locations"],"summary":"List locations","parameters":[{"name":"search","in":"query","schema":{"type":"string","maxLength":100}},{"name":"isActive","in":"query","schema":{"type":"string","enum":["true","false"]}},{"$ref":"#/components/parameters/pageParam"},{"$ref":"#/components/parameters/pageSizeParam"}],"responses":{"200":{"description":"Paginated location list with assets","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Locations"],"summary":"Create a delivery site (with optional contacts, schedules, assets)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"companyId":{"type":"string","format":"uuid","description":"Owner of the new location: your company (default) or one of its subsidiaries."},"name":{"type":"string","maxLength":200},"locationType":{"type":"string","maxLength":50,"nullable":true},"street":{"type":"string","maxLength":200,"nullable":true},"city":{"type":"string","maxLength":100,"nullable":true},"state":{"type":"string","maxLength":100,"nullable":true},"zip":{"type":"string","maxLength":20,"nullable":true},"country":{"type":"string","maxLength":2,"default":"US"},"lat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"lng":{"type":"number","minimum":-180,"maximum":180,"nullable":true},"gateCode":{"type":"string","maxLength":50,"nullable":true},"accessInstructions":{"type":"string","maxLength":1000,"nullable":true},"monthlyBudget":{"type":"number","exclusiveMinimum":0,"maximum":9999999999.99,"multipleOf":0.01,"nullable":true,"description":"EC-4258 — monthly spend budget for this site, in dollars; null = no budget. The customer is alerted when invoiced month-to-date spend reaches its threshold (default 80%)."},"contacts":{"type":"array","maxItems":5,"items":{"type":"object"}},"schedules":{"type":"array","maxItems":7,"items":{"type":"object"}},"assets":{"type":"array","maxItems":50,"items":{"type":"object"}}}}}}},"responses":{"201":{"description":"Created location","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Location"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/locations/{id}/assets":{"post":{"tags":["Locations"],"summary":"Add an asset (tank) to an existing location","description":"A tank IS an Asset. Assets can otherwise only be supplied when the location is created (PUT /locations/{id} rejects the field), so this is how a dispatch system records a tank found at a site that already exists. The location is resolved under your company or its subsidiaries — a location id outside that family returns 404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"201":{"description":"Asset created"},"404":{"description":"Location not found in your org family"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/locations/{id}/assets/{assetId}":{"put":{"tags":["Locations"],"summary":"Update an asset (tank)","description":"Partial update. currentLevel is clamped to capacity, matching the portal guardrail.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Asset updated"},"404":{"description":"Location or asset not found in your org family"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections":{"get":{"tags":["Connections"],"summary":"List connections","parameters":[{"name":"direction","in":"query","description":"Filter by direction relative to caller","schema":{"type":"string","enum":["inbound","outbound","all"],"default":"all"}},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","INFO_REQUESTED","RESUBMITTED","ACCEPTED","DECLINED"]}},{"name":"search","in":"query","description":"Case-insensitive match on a connected company's name, DBA, or HQ city","schema":{"type":"string","maxLength":100}},{"$ref":"#/components/parameters/pageParam"},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":20}}],"responses":{"200":{"description":"Paginated connection list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/{id}/decline":{"post":{"tags":["Connections"],"summary":"Decline an incoming connection request (receiver only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Connection DECLINED","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Connection"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the receiving company can decline"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/companies/products":{"get":{"tags":["Companies"],"summary":"List company product catalog (branch keys with no rows inherit the HQ catalog read-only — EC-2013)","responses":{"200":{"description":"Product rows: id, code, name, category, isActive, createdAt, defaultUnitCode. unitPrice/config are back-office-owned and never exposed."}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Companies"],"summary":"Add product to catalog","responses":{"201":{"description":"Created"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]},"put":{"tags":["Companies"],"summary":"Reconcile the FULL active product set (EC-1256: absent rows deactivate; empty array deactivates all — the nightly back-office sync shape)","responses":{"200":{"description":"Reconcile summary"},"400":{"$ref":"#/components/responses/BadRequest"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/companies/products/{id}":{"put":{"tags":["Companies"],"summary":"Update product","responses":{"200":{"description":"Updated"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]},"delete":{"tags":["Companies"],"summary":"Remove product","responses":{"204":{"description":"Deleted"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/product-taxonomy/units":{"get":{"tags":["Product Taxonomy"],"summary":"List the platform-global unit-of-measure catalog accepted by order-line unitCode","parameters":[{"name":"includeInactive","in":"query","schema":{"type":"boolean"},"description":"Include retired units — they still resolve on historical order lines"}],"responses":{"200":{"description":"Unit rows: code, label, labelPlural, category, allowsFraction, sortOrder, isActive. Bare array — the catalog is small and platform-global, so there is nothing to page. `allowsFraction: false` means a fractional quantity on that unit is rejected."}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/allocations":{"get":{"tags":["Allocations"],"summary":"List allocations by terminal","responses":{"200":{"description":"Allocation list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Allocation"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Allocations"],"summary":"Upsert allocation state by natural key (terminal + product + period)","responses":{"200":{"description":"Allocation created or updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Allocation"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/allocations/{id}":{"put":{"tags":["Allocations"],"summary":"Update an allocation by id (capacity/allocated/status only — natural key is immutable)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Allocation updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Allocation"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/payments":{"get":{"tags":["Payments"],"summary":"List payments","parameters":[{"name":"role","in":"query","description":"View as issuer or payer","schema":{"type":"string","enum":["issuer","payer"],"default":"issuer"}},{"name":"invoiceId","in":"query","description":"Filter by invoice","schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Paginated payment list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/payments/{id}":{"get":{"tags":["Payments"],"summary":"Get payment by id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payment detail","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Payment"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/timeline":{"get":{"tags":["Orders"],"summary":"Order status history, oldest first. Owner, carrier or assigned broker; the poster and broker also see forwarded children's rows, and the downstream carrier is never named to the poster","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Status log rows","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","required":["id","fromStatus","toStatus","changedBy","notes","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"fromStatus":{"type":"string","nullable":true},"toStatus":{"type":"string"},"changedBy":{"type":"string","format":"uuid","nullable":true,"description":"Acting user id. Always null on /v1/public."},"notes":{"type":"string","nullable":true,"description":"The end-customer name is redacted for a carrier or broker reader (EC-1400). Always null on /v1/public."},"createdAt":{"type":"string","format":"date-time"},"actorParty":{"type":"string","enum":["poster","carrier","broker"],"nullable":true,"description":"Which party on the order made the change (EC-4811). Null for system changes."},"forward":{"type":"object","required":["party","brokerName","carrierName"],"properties":{"party":{"type":"string","enum":["broker","carrier"],"nullable":true},"brokerName":{"type":"string","nullable":true},"carrierName":{"type":"string","nullable":true,"description":"Null for the poster: the downstream carrier is never named to it."}},"description":"Only on rows merged in from a forwarded child order (EC-4085)."}}}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/forward":{"post":{"tags":["Orders"],"summary":"Broker duplicates an accepted (CONFIRMED) parent order into a child direct order assigned to a connected carrier — parent stays CONFIRMED (hop depth 1; a child cannot itself be forwarded)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["carrierCompanyId"],"properties":{"carrierCompanyId":{"type":"string","format":"uuid"},"terminal":{"type":"string","maxLength":200,"nullable":true,"description":"Override child line terminal"},"supplierId":{"type":"string","format":"uuid","nullable":true,"description":"Override child line supplier"},"supplierName":{"type":"string","minLength":1,"maxLength":200,"nullable":true,"description":"Ad-hoc supplier name for the child — send INSTEAD of supplierId, never alongside"},"loadingNumber":{"type":"string","minLength":1,"maxLength":100,"nullable":true,"description":"Free-text loading number stamped on every child line"},"freightRate":{"type":"number","minimum":0,"nullable":true}}}}}},"responses":{"201":{"description":"Child order created (ASSIGNED, pending carrier acceptance)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Order"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/po":{"patch":{"tags":["Orders"],"summary":"Set or change an order's PO number (own orders only), in any status except CANCELLED, including after delivery and invoicing (EC-1354). Linked invoices that are not void take the new PO. Null clears it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"poNumber":{"type":"string","maxLength":64,"nullable":true,"description":"Required on /v1/public (send null to clear)"}}}}}},"responses":{"200":{"description":"{ id, poNumber }"},"400":{"description":"Order is cancelled or is a forwarded copy"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The PO is already used on another of your orders"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoices/{id}":{"get":{"tags":["Invoices"],"summary":"Get invoice by id","security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invoice with line items and tax lines","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"x-classification":"PUBLIC"}},"/invoices/{id}/issue":{"post":{"tags":["Invoices"],"summary":"Issue a DRAFT invoice (DRAFT → OUTSTANDING; notifies the payer)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Invoice issued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"400":{"description":"Not a DRAFT, unpriced lines, or a pricing-subscription invoice (issued automatically and can't be edited)"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"API key lacks the invoices:write scope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/reconfirm":{"post":{"tags":["Orders"],"summary":"Reconfirm a haul after the poster edited material terms (assigned carrier)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Reconfirmed — needsReconfirmation cleared"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/accept":{"post":{"tags":["Orders"],"summary":"Accept an offered order (assigned carrier) — order becomes CONFIRMED","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"eta":{"type":"string","format":"date-time","nullable":true,"description":"Future ETA. Required when the poster's order settings demand an ETA at acceptance."},"erpId":{"type":"string","maxLength":120,"nullable":true,"description":"Your ERP code (station id) for this ship-to. Whenever sent it is stored on the connection for this delivery site, replacing any code held for it. Required (400 `ERP_ID_REQUIRED`) when your connection with the poster requires an ERP ID for every new ship-to and you have none for this site yet."}}}}}},"responses":{"200":{"description":"Accepted — { id, status: CONFIRMED, carrierCompanyId, acceptedEta }"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Load already taken by another carrier (broadcast race)"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/decline":{"post":{"tags":["Orders"],"summary":"Decline an offered order — waterfall advances or the board reopens","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Declined — { id, status, carrierCompanyId }"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/broker-accept":{"post":{"tags":["Orders"],"summary":"Broker accepts a load posted to it — ASSIGNED becomes CONFIRMED","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Accepted — { id, status: CONFIRMED }","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["CONFIRMED"]}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Order already accepted or declined"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/broker-decline":{"post":{"tags":["Orders"],"summary":"Broker declines a load posted to it — the order goes back to the poster as a DRAFT","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":1000,"description":"Optional reason, recorded on the assignment log"}}}}}},"responses":{"200":{"description":"Declined — { id, status: DRAFT }","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["DRAFT"]}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Order already accepted or declined"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/dispatch":{"post":{"tags":["Orders"],"summary":"Dispatch a driver (SMS/email) or self-dispatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Dispatched — { ok, orderId, driverFlowState, smsQueued, smsSent, smsError?, emailQueued, redispatch }. Internal-only fields (pushQueued, smsFallback, driverUserId — EC-1682 in-app dispatch) never appear here; the public schema cannot select the app channel."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Order status does not allow dispatch"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/loads/{id}/sourcing":{"patch":{"tags":["Orders"],"summary":"Fill blank pickup terminal/supplier per line (assigned carrier)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Sourcing written — poster-set attributes are never overwritten"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/confirm-pickup":{"post":{"tags":["Orders"],"summary":"Confirm pickup — multi-BOL payload (bols[] + per-line actuals) or empty body to advance state. On /v1/public/* it auto-advances the order to IN_TRANSIT; the portal route stops at CONFIRMED (Load complete) and start-transit is a separate step. Per line, the loaded-from supplier is supplierId (directory UUID) OR supplierName (ad-hoc plain text for a supplier not on the platform) — never both. lines[] is matched by orderLineIndex, never by product name: one entry per ordered line — set loadedProduct to the product name actually loaded when it differs from (or is just your name for) the ordered one; it is stored as a substitution and shown on the order. addedLines is for product loaded BEYOND the order. If you cannot map the ordered lines at all, send lines: [] with the whole load in addedLines — every ordered line is recorded as not loaded and the added lines carry the actuals UNPRICED (the office prices them by hand)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Load complete — { ok, orderId, status: CONFIRMED, driverFlowState: CONFIRMED, loadingNumbers }"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Order status / driver-flow state does not allow confirm-pickup"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/start-transit":{"post":{"tags":["Orders"],"summary":"Load complete → in transit. Required after a portal confirm-pickup, which stops at CONFIRMED (Load complete). Optional on /v1/public/* — confirm-pickup auto-advances there, and an order already in transit returns success","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"In transit — { ok, orderId, status: IN_TRANSIT, driverFlowState: IN_TRANSIT }"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Pickup not confirmed yet (status/driver-flow guard)"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/driver-position":{"get":{"tags":["Orders"],"summary":"The driver's most recent position on a load in transit, rounded to roughly a kilometre and carrying NO identity. Over the public API (EC-4781) only the poster's own key is served: the carrier's key and any other company's key get 404. In the portal the poster, the hauling carrier and the broker in between can see it, and a caller with no claim gets { position: null }. Otherwise position is null when there is no fix, when the newest fix is stale, or when the load is not moving","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"{ position: { latitude, longitude, recordedAt } | null, sharing: boolean | null } — sharing is the phone's own report for this load (false = the driver chose not to share; null = never reported), never inferred from missing points"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Public API only: not an order the key's company posted"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/locations/{id}/assets/{assetId}/history":{"get":{"tags":["Locations"],"summary":"A tank's level over time with the burn rate behind it. Readings come from the tank monitor (TankReading, EC-908) with outliers flagged by a rolling median + MAD, never deleted; fills come from delivered orders into the location for this tank's product. The baseline is a median (Theil–Sen over readings, or the median per-interval delivery rate) so a game-night spike never moves it. Which source wins follows the company's OrderSettings.tankLevelSource. Returns the reorder point and low limit as percentages, and days until each at the baseline rate. Public API (EC-4806): locations:read; the location must belong to your company or a subsidiary, otherwise 404","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":7,"maximum":365,"default":90}}],"responses":{"200":{"description":"{ asset, readings[], fills[], baseline: { galPerDay, method, source, samples }, projected: { daysToReorder, daysToLowLimit } }. Over the public API, readings[] holds at most 1,000 points: a denser window is cut into equal time slices and the last reading in each is returned. The baseline is computed from every reading."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/tracking-link":{"post":{"tags":["Orders"],"summary":"Mint a customer tracking link for a load you posted. The URL opens an auth-less page showing what is coming, the delivery window, the live ETA, a rough anonymous driver position while in transit, and proof-of-delivery files once delivered — never the carrier, driver, prices or document numbers. Minting again revokes every earlier link for the load, so a link already sent stops working. Not available for split loads. Over the public API (EC-4781) only the poster's own key can mint: the carrier's key and any other company's key get 404","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ url }"},"400":{"description":"The load is a split load (tracking links are per order, not per stop)"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The load is a draft or cancelled — nothing to track"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/notify-delivered":{"post":{"tags":["Orders"],"summary":"Report delivery to the originator (no status change; one notification per order)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ ok: true, alreadyNotified?: true }"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Only the hauling carrier can report delivery"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/assignee-cancel":{"post":{"tags":["Orders"],"summary":"Assignee escape hatch — cancel a committed order pre-delivery","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Cancelled — { id, status: CANCELLED }"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/eta":{"post":{"tags":["Orders"],"summary":"Set/update the carrier ETA on a confirmed order (non-blocking follow-up)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"ETA set — { id, acceptedEta }"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/orders/{id}/ship-to-erp-id":{"put":{"tags":["Orders"],"summary":"Set or replace your ERP code (station id) for an accepted haul's delivery site","description":"Assigned carrier only, accepted hauls only (offers and cancelled loads are 404). Stored on your connection with the poster and returned as `shipToErpId` on every later `role=carrier` read of an order to that site. Replaces any code held for the site, including one imported under a different spelling of the address. Send `deliveryStopId` (from the order's `deliveryStops[]`) to set one split-load stop.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["erpId"],"properties":{"erpId":{"type":"string","minLength":1,"maxLength":120},"deliveryStopId":{"type":"string","format":"uuid","nullable":true}}}}}},"responses":{"200":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","required":["orderId","shipToErpId"],"properties":{"orderId":{"type":"string","format":"uuid"},"deliveryStopId":{"type":"string","format":"uuid","nullable":true},"shipToErpId":{"type":"string"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"No connection with the poster to store the code on"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/invoices/{id}/mark-paid":{"put":{"tags":["Invoices"],"summary":"Mark invoice as paid","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkPaidRequest"}}}},"responses":{"200":{"description":"Invoice marked as paid","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Invoice"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"API key lacks the invoices:write scope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/terminal/{id}":{"get":{"tags":["Pricing"],"summary":"Rack prices for a specific terminal","parameters":[{"name":"id","in":"path","required":true,"description":"Terminal ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Terminal pricing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/RackPrice"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/contract":{"get":{"tags":["Pricing"],"summary":"Contract prices — role=supplier (own roster incl. scheduled) or role=customer (live rows issued to you)","parameters":[{"name":"role","in":"query","schema":{"type":"string","enum":["supplier","customer"],"default":"supplier"}},{"name":"customerId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"supplierId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Paginated rows { id, supplierId, customerId, product, terminalId, price, unit, effectiveAt, expiresAt, createdAt } — latest per (counterparty, product). role=customer also returns rows a supplier issued to an ANCESTOR of the caller (EC-3233: connections are org-family-inherited, so a subsidiary sees the pricing its HQ was given); the row keyed to the caller itself wins per product, and customerId is always the caller"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Pricing"],"summary":"Upsert contract prices for one connected customer (supplier side)","responses":{"200":{"description":"The written rows"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No ACCEPTED same-tier connection with that customer. EC-3236: a subsidiary of a connected organization counts — the connection may sit on any ancestor of the customer (or of the calling supplier); an explicit per-pair disconnect still vetoes"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/pricing/imports":{"post":{"tags":["Pricing"],"summary":"Preview a price-sheet import from an uploaded file (context import_csv)","responses":{"201":{"description":"PREVIEW batch — { batchId, source, parsedCount, dataCount, warnings, previewRows (first 50) }"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"fileUploadId not found or not owned by the caller"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/pricing/imports/{id}":{"get":{"tags":["Pricing"],"summary":"Import batch status + first-50 preview rows","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Batch { id, source, status, fileName, parsedRows, importedRows, skippedRows, warnings, previewRows, createdAt, committedAt }"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/imports/{id}/commit":{"post":{"tags":["Pricing"],"summary":"Commit a PREVIEW batch into RackPrice rows (single-shot)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ batchId, imported, skipped, newTerminals, cardedEligible, unresolvedTerminalCodes } — skipped counts invalid AND already-present rows. Unmapped terminal codes are auto-created (newTerminals) only when the key also holds supplier_terminals:write; otherwise their rows are skipped and the codes listed in unresolvedTerminalCodes (EC-2356)"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Batch already committed or cancelled"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/imports/{id}/cancel":{"post":{"tags":["Pricing"],"summary":"Cancel a PREVIEW batch (idempotent)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ ok: true }"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Committed batches cannot be cancelled"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/imports/terminal-aliases":{"get":{"tags":["Pricing"],"summary":"List terminal aliases (source feed code → your SupplierTerminal)","responses":{"200":{"description":"Alias rows"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Pricing"],"summary":"Create a terminal alias (used by the next commit)","responses":{"201":{"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"SupplierTerminal not found or not yours"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/pricing/imports/product-aliases":{"get":{"tags":["Pricing"],"summary":"List product aliases (raw sheet label → canonical product)","responses":{"200":{"description":"Alias rows"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Pricing"],"summary":"Create a product alias (used by the next commit)","responses":{"201":{"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/master-terminals":{"get":{"tags":["Reference Data"],"summary":"List IRS-seeded master terminals — the directory loading-number matching keys off","parameters":[{"name":"search","in":"query","schema":{"type":"string","maxLength":100},"description":"Match on terminal name / code / city"},{"name":"state","in":"query","schema":{"type":"string","maxLength":2},"description":"Two-letter state filter"},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}}],"responses":{"200":{"description":"Paginated terminal rows. IRS-seeded and active only — company-created (ADHOC) terminals that also live in this table are excluded; read your own under /supplier-terminals. Row timestamps and provenance are not exposed."},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/tax-rates":{"get":{"tags":["Tax Rates"],"summary":"List your tax-rate schedule (filter: category, activeOnly, effectiveAt)","responses":{"200":{"description":"Rate rows with productRules + customerRules (customer ids only — names never join)"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Tax Rates"],"summary":"Create a tax rate (PER_UNIT or PERCENTAGE, optional product/customer rules)","responses":{"201":{"description":"Created rate"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"A customerRule references a company without an ACCEPTED same-tier connection"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/tax-rates/{id}":{"get":{"tags":["Tax Rates"],"summary":"Get a tax rate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Rate row"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Tax Rates"],"summary":"Update a tax rate (rule arrays are full-replacement; omit to leave unchanged)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Updated rate"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"delete":{"tags":["Tax Rates"],"summary":"Deactivate a tax rate (soft — history keeps referencing it)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ ok: true } — the rate stays readable with isActive: false"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/pricing/freight-calculate":{"post":{"tags":["Pricing"],"summary":"Calculate freight for an origin/destination/gallons combination","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreightCalculateRequest"}}}},"responses":{"200":{"description":"Freight quote","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/FreightQuote"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/{id}/loading-numbers":{"get":{"tags":["Connections"],"summary":"Loading-number catalog for a connection — both directions, supplierId-attributed","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Rows { id, supplierId (issuing side), terminal, destinationState, loadingNumber, updatedAt }; markers have destinationState null + empty loadingNumber"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Connections"],"summary":"Full-replace your OWN direction of the loading-number roster (rows the counterparty issued you)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"The full catalog after the replace (both directions)"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Branch caller on a shared HQ connection"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/{id}":{"get":{"tags":["Connections"],"summary":"Get connection by id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Connection detail","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Connection"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/requests":{"post":{"tags":["Connections"],"summary":"Send a connection request to another company","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Name the target by id OR by its connect code (the QR share token): send at least one, or the request is 400. A code that resolves to no active company answers 400 \"Invalid connect code\".","properties":{"toCompanyId":{"type":"string","format":"uuid","description":"Target company id. Omit when sending connectCode."},"connectCode":{"type":"string","maxLength":20,"description":"Target's connect code, as returned on the company share card; case-insensitive. Resolved server-side."},"message":{"type":"string","maxLength":1000,"description":"Optional note shown to the recipient."}}}}}},"responses":{"201":{"description":"Connection request created (PENDING)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Connection"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/connections/{id}/accept":{"post":{"tags":["Connections"],"summary":"Accept an incoming pending connection request","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Connection accepted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Connection"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/{id}/disconnect":{"post":{"tags":["Connections"],"summary":"Sever an ACCEPTED connection (either party; data preserved)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Connection DISCONNECTED — reconnect goes through the request flow"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/connections/{id}/freight-rates":{"get":{"tags":["Connections"],"summary":"List freight rates for a connection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Freight rate list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ConnectionFreightRate"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Connections"],"summary":"Create freight rate on a connection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFreightRateRequest"}}}},"responses":{"201":{"description":"Freight rate created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/ConnectionFreightRate"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/freight-rates/{id}":{"put":{"tags":["Connections"],"summary":"Update a freight rate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFreightRateRequest"}}}},"responses":{"200":{"description":"Updated freight rate","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/ConnectionFreightRate"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"delete":{"tags":["Connections"],"summary":"Delete a freight rate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/delivery-tickets":{"get":{"tags":["Orders"],"summary":"List delivery tickets","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["DRAFT","ISSUED","SIGNED","VOID"]}},{"name":"orderId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"updatedSince","in":"query","description":"Public API only. Return rows whose updatedAt is at or after this ISO 8601 date-time, ordered by updatedAt then id (ascending). sortBy, sortOrder and page are ignored, and meta becomes { pageSize, hasMore, nextCursor }. Walk by passing meta.nextCursor back as `cursor` until hasMore is false; an unchanged row is never skipped or repeated, and a row changed during the walk comes back again later with its new state. Keep the last nextCursor to resume the next sync run. Rows changed in the last 60 seconds are held back until a later page or run, so a change still being committed is never passed over. A walk reports changes only: a row that is deleted or discarded, or that leaves the filter (cancelled, unassigned, or moved to a status you did not ask for), simply stops appearing, so send status=all on order syncs and reconcile missing rows by id. Viewing or re-sending an invoice also moves its updatedAt, so expect harmless repeats.","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"Public API only. The meta.nextCursor of the previous page of an updatedSince walk. Opaque; requires updatedSince (400 otherwise, and 400 for a malformed cursor).","schema":{"type":"string","maxLength":200}},{"$ref":"#/components/parameters/pageParam"},{"$ref":"#/components/parameters/pageSizeParam"}],"responses":{"200":{"description":"Paginated delivery ticket list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryTicket"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"},"totalPages":{"type":"integer"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true,"description":"Public API, updatedSince walks only: pass as `cursor` (with the same updatedSince) for the next page. Points at the last row returned (never at a row changed in the last 60 seconds); store it to resume the next sync run from there."}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Orders"],"summary":"Create delivery ticket","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeliveryTicketRequest"}}}},"responses":{"201":{"description":"Delivery ticket created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DeliveryTicket"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/delivery-tickets/{id}":{"get":{"tags":["Orders"],"summary":"Get delivery ticket","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Delivery ticket detail","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DeliveryTicket"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Orders"],"summary":"Update delivery ticket","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeliveryTicketRequest"}}}},"responses":{"200":{"description":"Updated delivery ticket","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DeliveryTicket"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/delivery-tickets/{id}/pdf":{"get":{"tags":["Orders"],"summary":"Download delivery ticket PDF","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"PDF binary","content":{"application/pdf":{}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/delivery-tickets/{id}/sign":{"post":{"tags":["Orders"],"summary":"Sign delivery ticket (driver / receiver)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignDeliveryTicketRequest"}}}},"responses":{"200":{"description":"Signed delivery ticket","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/DeliveryTicket"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/supplier-terminals":{"get":{"tags":["Supplier Terminals"],"summary":"List supplier terminals","parameters":[{"name":"search","in":"query","schema":{"type":"string","maxLength":100}},{"name":"state","in":"query","description":"Filter by state (2-letter code)","schema":{"type":"string","maxLength":2}},{"$ref":"#/components/parameters/pageParam"},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Paginated supplier terminal list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SupplierTerminal"}},"meta":{"$ref":"#/components/schemas/PaginatedResponse/properties/meta"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"post":{"tags":["Supplier Terminals"],"summary":"Create a supplier terminal (EC-741)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertTerminalRequest"}}}},"responses":{"201":{"description":"Terminal created (adhoc codes also mirror into the global directory)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/SupplierTerminal"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/supplier-terminals/{id}":{"get":{"tags":["Supplier Terminals"],"summary":"Get supplier terminal detail","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Supplier terminal","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/SupplierTerminal"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Supplier Terminals"],"summary":"Update a supplier terminal (EC-741)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertTerminalRequest"}}}},"responses":{"200":{"description":"Updated terminal","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/SupplierTerminal"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"delete":{"tags":["Supplier Terminals"],"summary":"Archive a supplier terminal (soft — stays GET-by-id resolvable with isActive: false)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"{ ok: true }. Fires the terminal_access.updated webhook (action deactivated) to the supplier and the connected companies approved for that terminal."},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/locations/{id}":{"get":{"tags":["Locations"],"summary":"Get location detail","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Location with contacts and assets","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Location"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Locations"],"summary":"Update a delivery site (partial; isActive toggles archiving from listings)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Any subset of the create fields EXCEPT assets (create-time only — sending it is a 400), plus isActive (boolean). contacts and schedules replace in full when provided."}}}},"responses":{"200":{"description":"Updated location","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Location"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/companies/{id}/profile":{"get":{"tags":["Companies"],"summary":"Get a company public profile (own company via tenant scope)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Company profile","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"$ref":"#/components/schemas/CompanyProfile"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Companies"],"summary":"Update your company profile (strict allowlist mirroring the read shape; unknown fields 400)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"responses":{"200":{"description":"Updated profile (same shape as the GET). Changing hq* re-geocodes lat/lng server-side."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/companies/services":{"get":{"tags":["Companies"],"summary":"List your services catalog","responses":{"200":{"description":"Service rows: id, code, name, category, isActive, createdAt"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"},"put":{"tags":["Companies"],"summary":"Reconcile the FULL active service set (absent rows deactivate; empty array deactivates all)","responses":{"200":{"description":"Reconcile summary"},"400":{"$ref":"#/components/responses/BadRequest"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/usage/me":{"get":{"tags":["API Keys"],"summary":"Own quota + burst state for the calling key (platform:usage_read — auto-granted on every key)","responses":{"200":{"description":"Current month usage, monthly cap, remaining, per-minute burst limit; non-billable call"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/me":{"get":{"tags":["API Keys"],"summary":"The company the calling key acts for, its scopes, and whether it is a sandbox key (platform:usage_read — auto-granted on every key)","description":"Non-billable. For an `ec_test_` key, `company` is the sandbox company its requests run as, never the owning company. `credential.type` is `api_key`; `actingUser` is null for API keys (it names the consenting user for OAuth tokens). `company.roles` lists the portal roles the company holds: `carrier`, `supplier`, `broker`, `customer`.","responses":{"200":{"description":"`company` { id, name, roles[] }, `credential` { type, id, keyPrefix, name, scopes[], sandbox, expiresAt }, `actingUser`"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC"}},"/api-keys/me/rotate":{"post":{"tags":["API Keys"],"summary":"Rotate the calling key (platform_api_keys:manage) — returns the successor raw key ONCE; the calling key stays valid for 24h, and cannot be rotated again","responses":{"200":{"description":"`newKey` (same scopes, IP allowlist and key type; `rawKey` shown once) + `oldKeyGraceExpiresAt`"},"400":{"description":"Key already rotated, revoked or expired"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Key lacks platform_api_keys:manage"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/api-keys/me/revoke":{"post":{"tags":["API Keys"],"summary":"Revoke the calling key immediately (platform_api_keys:manage) — cannot be undone","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["compromised","no_longer_needed","other"],"description":"Why the key is revoked; recorded in the audit log."}}}}}},"responses":{"200":{"description":"The revoked key (no secret) plus `rotatedDescendants`: the live keys rotated from it, transitively, each { id, name, keyPrefix, revoked: false }. This call never revokes them; revoke them in the portal."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Key lacks platform_api_keys:manage"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-public":true,"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-classification":"PUBLIC","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}]}},"/orders/{id}/bids/{bidId}/accept":{"put":{"tags":["Orders"],"summary":"PUT /orders/{id}/bids/{bidId}/accept","operationId":"put-orders-id-bids-bidid-accept","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:364`. Middleware: authenticate, apiLimiter, tenantScope, requireRole, auditLog.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"bidId","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/{id}/bids/{bidId}/counter":{"put":{"tags":["Orders"],"summary":"PUT /orders/{id}/bids/{bidId}/counter","operationId":"put-orders-id-bids-bidid-counter","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:345`. Middleware: authenticate, apiLimiter, tenantScope, requireRole, bidIdParam, auditLog.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"bidId","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/{id}/bids/{bidId}/re-counter":{"put":{"tags":["Orders"],"summary":"PUT /orders/{id}/bids/{bidId}/re-counter","operationId":"put-orders-id-bids-bidid-re-counter","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:383`. Middleware: authenticate, apiLimiter, requireRole, bidIdParam, auditLog.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"bidId","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/{id}/bids/{bidId}/withdraw":{"put":{"tags":["Orders"],"summary":"PUT /orders/{id}/bids/{bidId}/withdraw","operationId":"put-orders-id-bids-bidid-withdraw","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:401`. Middleware: authenticate, apiLimiter, requireRole, auditLog.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"bidId","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Unique key that makes this write safe to retry. 16-200 characters of [A-Za-z0-9_-] — a UUID works. Retrying with the SAME key and the same body replays the original response (with Idempotency-Replayed: true) without re-running side effects; the record is kept 24h. Reusing a key with a DIFFERENT body returns 409 IDEMPOTENCY_KEY_CONFLICT, so use a fresh key per distinct request. Omitting it returns 400 IDEMPOTENCY_KEY_REQUIRED.","schema":{"type":"string","minLength":16,"maxLength":200,"pattern":"^[A-Za-z0-9_-]{16,200}$"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/{id}/my-bid":{"get":{"tags":["Orders"],"summary":"GET /orders/{id}/my-bid","operationId":"get-orders-id-my-bid","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:313`. Middleware: authenticate, apiLimiter.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}},"/orders/my-bids":{"get":{"tags":["Orders"],"summary":"GET /orders/my-bids","operationId":"get-orders-my-bids","description":"Auto-generated stub from EC-271 inventory. Source: `backend/src/modules/orders/orders.routes.ts:146`. Middleware: authenticate, apiLimiter.","x-classification":"PUBLIC","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKeyAuth":[]},{"bearerAuth":[]}],"x-public":true}}}}