Skip to main content

API module

Base URLs:
  • Live: https://api.waysdrop.com
  • Staging: https://staging-api.waysdrop.com
Base path: /api This module is intended for third‑party integrations. All endpoints require the api-key header.

Response shapes

Success

No content

Some endpoints return 204 No Content with an empty body.

Errors

Errors are returned using the global error envelope described in Errors.

Conventions

  • Decimals — Prisma Decimal fields serialize as strings in JSON (e.g. "1650.00", "2.00000").
  • Coordinates — Resolved locations use flat lat / lon numbers, not a nested loc object.
  • Optional fields — Geo/admin fields on resolved addresses vary by what was geocoded; omitted keys are not returned.

Origin and destination

Endpoints that accept origin and destination (POST /api/route, POST /api/pricing, POST /api/request) use the same address model. You can send a plain string, a minimal geocode object, or a full resolved object from your map picker.

Request input

Legacy string — geocoded server-side:
Minimal object — preferred when you have coordinates or a place ID:
Full object — pass the resolved shape from your map picker or a prior /api/route response. Extra geo/admin fields are preserved; geocoding uses address (or addressLine1), lat/lon, and googlePlaceId. On POST /api/request, origin contact fields can also be set at the top level (originContactName, originContactPhone, originContactEmail). Top-level destination contact fields are required unless already present on the destination object.

Resolved address object

After geocoding (in /api/route responses, delivery records, etc.), origin and destination resolve to this shape. All fields except country are optional — presence depends on the geocode result.
Field groups Persisted addresses on deliveries (GET /api/deliveries/:deliveryId, POST /api/request → delivery.origin / delivery.destination) include id and timestamps in addition to the fields above.

Endpoints

GET /api/countries

Fetch available countries for routing and pricing. Query
  • search (optional, string)
200 Response
Response fields Without search, returns up to 50 results.

GET /api/states

Fetch available states. Query
  • search (optional, string)
200 Response
Response fields

GET /api/cities

Fetch available cities. Query
  • search (optional, string)
200 Response
Response notes
  • value is googlePlaceId when available, otherwise locationId.
  • lat / lon are the coordinates used for distance and ETA calculations.
  • Without search, returns up to 50 results.

POST /api/route

Get route data between two addresses. Body
200 Response
Response notes
  • origin and destination use the resolved address object shape. Contact fields are not included unless you sent them on input.
  • routeType is one of INTER_CITY, INTER_STATE, INTER_REGION, INTER_COUNTRY, or INTER_CONTINENT.
  • distance.distanceKm and distance.etaSeconds use the max of line and road distance for safety.

GET /api/fleet-types

Fetch available fleet types. 200 Response
Only active fleet types are returned. icon and description may be null.

POST /api/pricing

Get pricing for a delivery request. Two pricing modes are supported — use one or the other. Mode A — by package IDs (preferred) Resolves weight and value from your saved packages. Matches the pricing logic used by POST /api/request. Requires an authenticated user (API key owner).
Mode B — by weight and value Provide totals directly when you do not have saved packages.
Body fields

Mode A response — packagesId

200 Response
Mode A response notes
  • costs.total = deliverySubtotal + serviceFee (+ hub commission when applicable).
  • deliveryFee describes who pays what before a request is created.
  • matchingEligible is true when courier matching can start immediately after payment.
  • When courierSelection is SPECIFIC, a courier object may also be included.
  • Auto-promotions are not applied on this endpoint.

Mode B response — totalWeight + totalValue

200 Response
When a specific courier has a matching pricing rule, courierPricingRule is populated and weight/fleet/surcharge may be zeroed:
Common errors
  • 400 Provide packagesId or both totalWeight and totalValue
  • 400 Authenticated user is required when pricing by packagesId

POST /api/request

Create a delivery request (P2P). Body
When using the full object from your map picker, contact fields on origin / destination satisfy contact requirements — top-level originContact* / destinationContact* fields are optional in that case. Body fields Courier selection
  • ANYONE — any available courier on the platform
  • SPECIFIC — assign a courier you already have in the system (courierId required)
  • MERCHANT_SPECIAL_COURIERS — assign from your merchant-specific courier list (requires couriers added in the merchant dashboard)

Response — paymentMethodType: WALLET (default)

201 Response

Response — paymentMethodType: CHECKOUT

Returns a Paystack checkout payload instead of debiting the wallet immediately:
Courier matching starts after payment succeeds.

Response — payOnDelivery: true

Adds a payOnDelivery block:
Response notes
  • totalWeight and totalValue on the P2P record are strings (Decimal).
  • delivery.distanceKm is a number (float).
  • delivery.status is always REQUEST_CREATED on create; status on the P2P record reflects payment state (PENDING, PENDING_PAYMENT, etc.).
  • Full origin / destination use the resolved address object plus id and timestamps.
Common errors
  • 422 Insufficient balance
  • 400 Invalid packages selection
  • 400 Origin and destination cannot be the same
  • 404 Fleet type not found
  • 404 No merchant specific couriers found (when using MERCHANT_SPECIAL_COURIERS without configured couriers)

POST /api/request/:deliveryId/cancel

Cancel an eligible delivery request. Path params
  • deliveryId (uuid)
201 Response
Common errors
  • 404 Delivery request not found
  • 400 Delivery request can not be canceled due to its current status
  • 400 You can only cancel a delivery request twice every 3 hours

POST /api/package

Create or edit a package. Body Use this endpoint to create a new package or update an existing one.
  • To create, omit packageId and provide all required fields.
  • To update, include packageId and provide only the fields you want to change.
Create example
Update example
Fields Weight is derived from size on create — you do not send weight directly. 201 Response

DELETE /api/package/:packageId

Delete a package. Path params
  • packageId (uuid)
204 Response
  • No content

GET /api/packages

Get packages that have not been assigned to a delivery (for the authenticated user). 200 Response
Returns the full DeliveryPackage record for each unassigned package.

GET /api/deliveries/:deliveryId

Get a delivery (for the authenticated user). Path params
  • deliveryId (uuid)
200 Response
Response notes
  • The delivery object includes all scalar Delivery fields — the example shows the most useful ones.
  • origin and destination use the full resolved address object with id and timestamps.
  • courier is omitted (not null) until a courier is assigned.
  • currentLocation uses flat lat / lon, not a nested loc object.
  • Proof code is null until the proof has been scanned (scannedAt is set).
  • deliverySteps only includes visible steps (hidden steps are excluded).
  • p2pDelivery.totalWeight and totalValue are strings (Decimal).