{"components":{"responses":{},"schemas":{"Address":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"Courier":{"description":"A courier company available to the tenant, with capabilities.","properties":{"active_drivers":{"nullable":true,"type":"integer"},"base_eta_minutes":{"nullable":true,"type":"integer"},"base_fee_aoa":{"nullable":true,"type":"integer"},"capabilities":{"properties":{"bank_account_holder":{"nullable":true,"type":"string"},"bank_iban":{"nullable":true,"type":"string"},"bank_name":{"nullable":true,"type":"string"},"bank_transfer_enabled":{"type":"boolean"},"max_weight_grams":{"nullable":true,"type":"integer"},"parcel_types":{"items":{"type":"string"},"nullable":true,"type":"array"},"require_delivery_pin":{"type":"boolean"}},"type":"object"},"id":{"format":"uuid","type":"string"},"logo_url":{"format":"uri","nullable":true,"type":"string"},"name":{"example":"Luanda Express","type":"string"},"operating_hours":{"additionalProperties":true,"nullable":true,"type":"object"},"service_zones":{"description":"Cities or zones this courier covers.","items":{"type":"string"},"type":"array"},"slug":{"example":"luanda-express","type":"string"}},"required":["id","name"],"title":"Courier","type":"object"},"CourierListResponse":{"description":"Envelope returned by GET /v1/couriers.","properties":{"data":{"items":{"description":"A courier company available to the tenant, with capabilities.","properties":{"active_drivers":{"nullable":true,"type":"integer"},"base_eta_minutes":{"nullable":true,"type":"integer"},"base_fee_aoa":{"nullable":true,"type":"integer"},"capabilities":{"properties":{"bank_account_holder":{"nullable":true,"type":"string"},"bank_iban":{"nullable":true,"type":"string"},"bank_name":{"nullable":true,"type":"string"},"bank_transfer_enabled":{"type":"boolean"},"max_weight_grams":{"nullable":true,"type":"integer"},"parcel_types":{"items":{"type":"string"},"nullable":true,"type":"array"},"require_delivery_pin":{"type":"boolean"}},"type":"object"},"id":{"format":"uuid","type":"string"},"logo_url":{"format":"uri","nullable":true,"type":"string"},"name":{"example":"Luanda Express","type":"string"},"operating_hours":{"additionalProperties":true,"nullable":true,"type":"object"},"service_zones":{"description":"Cities or zones this courier covers.","items":{"type":"string"},"type":"array"},"slug":{"example":"luanda-express","type":"string"}},"required":["id","name"],"title":"Courier","type":"object"},"type":"array"}},"required":["data"],"title":"CourierListResponse","type":"object"},"DeliveryEvent":{"description":"Append-only event in a delivery's history.","properties":{"event_type":{"description":"Dotted event type; matches the webhook event types.","example":"delivery.assigned","type":"string"},"id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"payload":{"additionalProperties":true,"description":"Event-specific payload.","nullable":true,"type":"object"},"source":{"description":"Origin of the event (system, fleet_webhook, api, etc).","example":"fleet_webhook","nullable":true,"type":"string"}},"required":["event_type","occurred_at"],"title":"DeliveryEvent","type":"object"},"DeliveryListResponse":{"description":"Paginated list returned by GET /v1/deliveries.","properties":{"data":{"items":{"description":"Trimmed delivery shape returned in list endpoints (no events).","properties":{"courier":{"nullable":true,"properties":{"name":{"nullable":true,"type":"string"},"tracking_ref":{"nullable":true,"type":"string"}},"type":"object"},"destination":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"origin":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"},"quoted_fee_aoa":{"nullable":true,"type":"integer"},"reference":{"example":"DLV-2026-00042","type":"string"},"status":{"enum":["awaiting_payment","pending","assigned","pickup_ready","in_transit","out_for_delivery","delivered","cancelled","failed","stalled"],"type":"string"}},"required":["id","reference","status","inserted_at"],"title":"DeliverySummary","type":"object"},"type":"array"},"pagination":{"description":"Pagination metadata. `has_more` is computed via a limit+1 fetch — there is no `total` count.","properties":{"has_more":{"example":true,"type":"boolean"},"limit":{"example":50,"type":"integer"},"offset":{"example":0,"type":"integer"}},"required":["limit","offset","has_more"],"title":"Pagination","type":"object"}},"required":["data","pagination"],"title":"DeliveryListResponse","type":"object"},"DeliveryRequest":{"description":"Body for POST /v1/deliveries. Pricing is server-authoritative via `quote_id`;\nrequests carrying a `courier_fee_aoa` field are rejected with 422.\n","properties":{"cash_collect_aoa":{"description":"Required for `cod`; amount to collect from the recipient (centavos).","nullable":true,"type":"integer"},"company_id":{"description":"Optional. If present, must match the company the quote was issued for.","format":"uuid","type":"string"},"destination":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"options":{"properties":{"metadata":{"additionalProperties":true,"description":"Free-form key-value metadata, echoed back on responses and webhooks.","type":"object"}},"type":"object"},"origin":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"},"payment_method":{"default":"prepaid","enum":["prepaid","bank_transfer","cod"],"type":"string"},"quote_id":{"description":"Required. Returned from POST /v1/quotes; locks the fee.","example":"8c1e1a90-7f08-4b2c-9d5d-9a4ab7f59c0e","format":"uuid","type":"string"},"scheduled_delivery_at":{"format":"date-time","nullable":true,"type":"string"},"scheduled_pickup_at":{"format":"date-time","nullable":true,"type":"string"}},"required":["quote_id","origin","destination","parcel"],"title":"DeliveryRequest","type":"object"},"DeliveryResponse":{"description":"Full delivery, returned by POST /v1/deliveries and GET /v1/deliveries/:id.","properties":{"cash_collect_aoa":{"nullable":true,"type":"integer"},"courier":{"nullable":true,"properties":{"name":{"example":"Luanda Express","nullable":true,"type":"string"},"tracking_ref":{"example":"FLT-A4F2","nullable":true,"type":"string"}},"type":"object"},"destination":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"events":{"items":{"description":"Append-only event in a delivery's history.","properties":{"event_type":{"description":"Dotted event type; matches the webhook event types.","example":"delivery.assigned","type":"string"},"id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"payload":{"additionalProperties":true,"description":"Event-specific payload.","nullable":true,"type":"object"},"source":{"description":"Origin of the event (system, fleet_webhook, api, etc).","example":"fleet_webhook","nullable":true,"type":"string"}},"required":["event_type","occurred_at"],"title":"DeliveryEvent","type":"object"},"type":"array"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"origin":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"},"payment_method":{"enum":["prepaid","bank_transfer","cod"],"type":"string"},"quoted_fee_aoa":{"nullable":true,"type":"integer"},"reference":{"example":"DLV-2026-00042","type":"string"},"scheduled_delivery_at":{"format":"date-time","nullable":true,"type":"string"},"scheduled_pickup_at":{"format":"date-time","nullable":true,"type":"string"},"sla_deadline_at":{"format":"date-time","nullable":true,"type":"string"},"status":{"enum":["awaiting_payment","pending","assigned","pickup_ready","in_transit","out_for_delivery","delivered","cancelled","failed","stalled"],"type":"string"},"tracking_url":{"description":"Public tracking page for the recipient.","example":"https://entrega.ao/tracking/DLV-2026-00042","format":"uri","type":"string"}},"required":["id","reference","status","origin","destination","parcel","inserted_at"],"title":"DeliveryResponse","type":"object"},"DeliverySummary":{"description":"Trimmed delivery shape returned in list endpoints (no events).","properties":{"courier":{"nullable":true,"properties":{"name":{"nullable":true,"type":"string"},"tracking_ref":{"nullable":true,"type":"string"}},"type":"object"},"destination":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"origin":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"},"quoted_fee_aoa":{"nullable":true,"type":"integer"},"reference":{"example":"DLV-2026-00042","type":"string"},"status":{"enum":["awaiting_payment","pending","assigned","pickup_ready","in_transit","out_for_delivery","delivered","cancelled","failed","stalled"],"type":"string"}},"required":["id","reference","status","inserted_at"],"title":"DeliverySummary","type":"object"},"ErrorResponse":{"description":"RFC 7807 problem-detail body returned for 4xx/5xx responses.","properties":{"detail":{"example":"One or more fields are invalid","type":"string"},"error":{"description":"Machine-readable error code (e.g. `quote_expired`).","example":"quote_expired","nullable":true,"type":"string"},"errors":{"description":"Per-field details for validation errors.","items":{"additionalProperties":true,"type":"object"},"nullable":true,"type":"array"},"status":{"example":422,"type":"integer"},"title":{"example":"Validation Error","type":"string"},"type":{"example":"https://api.entrega.ao/errors/unprocessable_entity","format":"uri","type":"string"}},"required":["type","title","status","detail"],"title":"ErrorResponse","type":"object"},"EventListResponse":{"description":"All events for a delivery, oldest first.","properties":{"data":{"items":{"description":"Append-only event in a delivery's history.","properties":{"event_type":{"description":"Dotted event type; matches the webhook event types.","example":"delivery.assigned","type":"string"},"id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"payload":{"additionalProperties":true,"description":"Event-specific payload.","nullable":true,"type":"object"},"source":{"description":"Origin of the event (system, fleet_webhook, api, etc).","example":"fleet_webhook","nullable":true,"type":"string"}},"required":["event_type","occurred_at"],"title":"DeliveryEvent","type":"object"},"type":"array"}},"required":["data"],"title":"EventListResponse","type":"object"},"Pagination":{"description":"Pagination metadata. `has_more` is computed via a limit+1 fetch — there is no `total` count.","properties":{"has_more":{"example":true,"type":"boolean"},"limit":{"example":50,"type":"integer"},"offset":{"example":0,"type":"integer"}},"required":["limit","offset","has_more"],"title":"Pagination","type":"object"},"Parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"},"QuoteRequest":{"description":"Body for POST /v1/quotes. Returns one quote per available courier company.","properties":{"destination":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"origin":{"description":"Pickup or drop-off address with optional coordinates and contact.","properties":{"address_line":{"example":"Rua Marien Nguabi 42, Maianga","type":"string"},"city":{"example":"Luanda","type":"string"},"contact":{"properties":{"name":{"example":"João Silva","type":"string"},"phone":{"example":"+244923000000","type":"string"}},"type":"object"},"coordinates":{"properties":{"lat":{"example":-8.8159,"format":"float","type":"number"},"lng":{"example":13.2306,"format":"float","type":"number"}},"type":"object"},"province":{"example":"Luanda","type":"string"}},"required":["address_line","city","province"],"title":"Address","type":"object"},"parcel":{"description":"Parcel attributes used for capability matching and pricing.","properties":{"declared_value_aoa":{"description":"Declared value in AOA centavos.","example":15000,"type":"integer"},"description":{"example":"Photo prints","type":"string"},"height_cm":{"example":10,"type":"integer"},"length_cm":{"example":30,"type":"integer"},"weight_grams":{"example":1200,"type":"integer"},"width_cm":{"example":20,"type":"integer"}},"required":["weight_grams"],"title":"Parcel","type":"object"}},"required":["origin","destination","parcel"],"title":"QuoteRequest","type":"object"},"QuoteResponse":{"description":"Envelope returned by POST /v1/quotes — one entry per courier company.","properties":{"data":{"items":{"properties":{"company_id":{"format":"uuid","type":"string"},"company_logo_url":{"format":"uri","nullable":true,"type":"string"},"company_name":{"example":"Luanda Express","type":"string"},"company_slug":{"example":"luanda-express","type":"string"},"eta_minutes":{"example":45,"type":"integer"},"fee_aoa":{"example":3500,"type":"integer"},"quote_id":{"description":"Persisted Entrega quote id. Pass to POST /v1/deliveries as `quote_id` to lock this fee.","example":"8c1e1a90-7f08-4b2c-9d5d-9a4ab7f59c0e","format":"uuid","type":"string"},"require_delivery_pin":{"example":false,"type":"boolean"},"valid_until":{"format":"date-time","type":"string"}},"required":["quote_id","valid_until","company_id","fee_aoa","eta_minutes"],"type":"object"},"type":"array"}},"required":["data"],"title":"QuoteResponse","type":"object"},"TrackingResponse":{"description":"Public tracking shape — intentionally minimal (no internal data).\nReturned by GET /v1/deliveries/:delivery_id/tracking (no auth).\n","properties":{"events":{"items":{"properties":{"event_type":{"example":"delivery.in_transit","type":"string"},"occurred_at":{"format":"date-time","type":"string"}},"required":["event_type","occurred_at"],"type":"object"},"type":"array"},"reference":{"example":"DLV-2026-00042","type":"string"},"status":{"example":"in_transit","type":"string"}},"required":["reference","status","events"],"title":"TrackingResponse","type":"object"}},"securitySchemes":{"publishableKey":{"description":"Publishable key (`pk_live_*`) for browser-side widget calls. Origin-validated.","scheme":"bearer","type":"http"},"secretKey":{"description":"Secret key (`sk_live_*`) issued from the developer dashboard.","scheme":"bearer","type":"http"}}},"info":{"description":"REST API for tenants integrating with Entrega Express in Angola.\n\nTwo authenticated surfaces:\n\n- **Merchant API** (`/v1/*`): server-to-server. Authenticate with a\n  secret key (`sk_live_*`) as a Bearer token.\n- **Widget API** (`/v1/widget/*`): browser-side. Authenticate with a\n  publishable key (`pk_live_*`) as a Bearer token. Origin-validated.\n\nAll monetary values are integer AOA centavos. Pricing is server-authoritative\nvia persisted quotes — `POST /v1/deliveries` requires a `quote_id` from a\nprior `POST /v1/quotes` call and rejects any tenant-supplied `courier_fee_aoa`.\n","title":"Entrega Express API","version":"1.0.0"},"openapi":"3.0.3","paths":{"/v1/couriers":{"get":{"callbacks":{},"description":"Returns the courier companies available for this tenant, with capabilities (bank transfer, parcel limits, etc).","operationId":"EntregaWeb.Api.V1.CourierController.index","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourierListResponse"}}},"description":"Courier list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"}},"security":[{"secretKey":[]}],"summary":"List available courier companies","tags":["Couriers"]}},"/v1/deliveries":{"get":{"callbacks":{},"description":"Paginated list of the tenant's deliveries.\n`has_more` is computed via a limit+1 fetch — no `total` count is returned.\n","operationId":"EntregaWeb.Api.V1.DeliveryController.index","parameters":[{"description":"Single status (`pending`) or comma-separated list (`pending,assigned,in_transit`).","in":"query","name":"status","required":false,"schema":{"type":"string"}},{"description":"Only return deliveries created on/after this timestamp.","in":"query","name":"inserted_at_gte","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"Only return deliveries created on/before this timestamp.","in":"query","name":"inserted_at_lte","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"Sort direction; defaults to `inserted_at_desc`.","in":"query","name":"sort","required":false,"schema":{"enum":["inserted_at_desc","inserted_at_asc"],"type":"string"}},{"description":"Substring match on reference, origin/destination city, and address line.","in":"query","name":"search","required":false,"schema":{"type":"string"}},{"description":"Page size; clamped to 100.","in":"query","name":"limit","required":false,"schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}},{"description":"Page offset; capped at 100,000 to prevent deep-pagination DoS.","in":"query","name":"offset","required":false,"schema":{"default":0,"maximum":100000,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryListResponse"}}},"description":"Paginated delivery list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"One or more query params are invalid"}},"security":[{"secretKey":[]}],"summary":"List deliveries","tags":["Deliveries"]},"post":{"callbacks":{},"description":"Requires `quote_id` from a prior `POST /v1/quotes` call. Pricing is server-authoritative;\nrequests carrying `courier_fee_aoa` are rejected. Origin/destination/parcel must match\nthe persisted quote — mismatches return 422 `quote_payload_mismatch`.\n","operationId":"EntregaWeb.Api.V1.DeliveryController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryRequest"}}},"description":"Delivery body","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryResponse"}}},"description":"Delivery created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Validation error (quote_id_required, quote_expired, quote_company_mismatch, quote_payload_mismatch, courier_fee_aoa_not_accepted, quote_not_found, or other field-level errors)"}},"security":[{"secretKey":[]}],"summary":"Create a delivery","tags":["Deliveries"]}},"/v1/deliveries/{delivery_id}/events":{"get":{"callbacks":{},"description":"Returns all events for a delivery in chronological order (oldest first).","operationId":"EntregaWeb.Api.V1.EventController.index","parameters":[{"description":"Delivery id.","in":"path","name":"delivery_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventListResponse"}}},"description":"Event list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"}},"security":[{"secretKey":[]}],"summary":"List events for a delivery","tags":["Events"]}},"/v1/deliveries/{delivery_id}/tracking":{"get":{"callbacks":{},"description":"Public-safe tracking shape — no authentication required, no internal data exposed.\nReturns reference, status, and the event timeline (event_type + occurred_at only).\n","operationId":"EntregaWeb.Api.V1.EventController.tracking","parameters":[{"description":"Delivery id.","in":"path","name":"delivery_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackingResponse"}}},"description":"Tracking shape"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"}},"security":[],"summary":"Public tracking view for a delivery","tags":["Events"]}},"/v1/deliveries/{id}":{"delete":{"callbacks":{},"description":"Only succeeds when the delivery is in `awaiting_payment`, `pending`, or `assigned`. Returns 409 otherwise.","operationId":"EntregaWeb.Api.V1.DeliveryController.delete","parameters":[{"description":"Delivery id.","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"example":"cancelled","type":"string"}},"type":"object"}}},"description":"Cancellation accepted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Delivery cannot be cancelled in its current state"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"State machine rejected the cancellation"}},"security":[{"secretKey":[]}],"summary":"Cancel a delivery","tags":["Deliveries"]},"get":{"callbacks":{},"description":"Returns the full delivery, including its event history.","operationId":"EntregaWeb.Api.V1.DeliveryController.show","parameters":[{"description":"Delivery id.","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryResponse"}}},"description":"Delivery"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"}},"security":[{"secretKey":[]}],"summary":"Get a delivery","tags":["Deliveries"]}},"/v1/quotes":{"post":{"callbacks":{},"description":"Returns one persisted quote per available courier company. Each entry includes\na `quote_id` that you must pass to `POST /v1/deliveries` to lock the fee.\nA failure on one company does not poison the rest — that company simply\nwon't appear in the response.\n","operationId":"EntregaWeb.Api.V1.QuoteController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"description":"Origin, destination, parcel","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}},"description":"One quote per available courier"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid API key"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid origin / destination / parcel"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Upstream courier service failed to respond"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"No courier service is configured for this deployment"}},"security":[{"secretKey":[]}],"summary":"Quote a delivery across all courier companies","tags":["Quotes"]}},"/v1/widget/couriers":{"get":{"callbacks":{},"description":"Same shape as `GET /v1/couriers` but trimmed for browser-side use. Authenticated with a publishable key (`pk_live_*`).","operationId":"EntregaWeb.Api.V1.Widget.CourierController.index","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourierListResponse"}}},"description":"Courier list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid publishable key, or origin not allowed"}},"security":[{"publishableKey":[]}],"summary":"List available courier companies (widget)","tags":["Widget"]}},"/v1/widget/deliveries":{"post":{"callbacks":{},"description":"Same shape as `POST /v1/deliveries` but for browser-side use. Metadata is auto-tagged with `\"source\": \"widget\"`.","operationId":"EntregaWeb.Api.V1.Widget.DeliveryController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryRequest"}}},"description":"Delivery body","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryResponse"}}},"description":"Delivery created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid publishable key, or origin not allowed"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Validation error"}},"security":[{"publishableKey":[]}],"summary":"Create a delivery (widget)","tags":["Widget"]}},"/v1/widget/deliveries/{id}":{"get":{"callbacks":{},"description":"Returns the delivery + event history. Trimmed for widget use.","operationId":"EntregaWeb.Api.V1.Widget.DeliveryController.show","parameters":[{"description":"Delivery id.","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryResponse"}}},"description":"Delivery"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid publishable key"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"}},"security":[{"publishableKey":[]}],"summary":"Get a delivery (widget)","tags":["Widget"]}},"/v1/widget/quotes":{"post":{"callbacks":{},"description":"Same shape as `POST /v1/quotes` but for browser-side use with a publishable key.","operationId":"EntregaWeb.Api.V1.Widget.QuoteController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"description":"Origin, destination, parcel","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}},"description":"One quote per available courier"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid publishable key, or origin not allowed"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid origin / destination / parcel"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Upstream courier service failed to respond"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"No courier service is configured"}},"security":[{"publishableKey":[]}],"summary":"Quote a delivery (widget)","tags":["Widget"]}}},"security":[{"secretKey":[]}],"servers":[{"description":"Production","url":"https://entrega.ao","variables":{}},{"description":"Development","url":"https://dev.entrega.ao","variables":{}}],"tags":[{"description":"Available courier companies and capabilities.","name":"Couriers"},{"description":"Fee quotes per courier company.","name":"Quotes"},{"description":"Create, list, fetch, and cancel deliveries.","name":"Deliveries"},{"description":"Per-delivery event history and public tracking.","name":"Events"},{"description":"Browser-side endpoints used by the embeddable widget (publishable-key auth).","name":"Widget"}]}