Back to API reference

Partner integration

Integrating with the JEK v1 API

This guide is written for third-party clients running on JEK infrastructure — Driver Pro (Driver Assistant) and M1OS. Everything below applies equally to the JEK app, the H5 site, the admin console and the public website; those simply use the user/admin surfaces instead of the device surface.

Environments

EnvironmentBase URLNotes
Productionhttps://api.jek.appLive traffic. Rate limited per device.
Staginghttps://api-staging.jek.appUse for certification before a firmware release.

Every path is versioned: /api/v1/…. A breaking change ships as /api/v2; v1 stays available for at least six months after v2 reaches general availability.

Request & response conventions

  • Paths use kebab-case plural resources — /api/v1/ride-orders/{ride_order_id}/cancel
  • Fields use snake_case; identifiers are UUIDs named id / <resource>_id
  • Timestamps are ISO 8601 UTC and always end in _at (created_at, accepted_at)
  • Money is an integer in minor units plus an ISO 4217 code: { "amount": 8850, "currency": "HKD" } = HK$88.50
  • Phone numbers are E.164 (+85268880042); countries ISO 3166-1 alpha-2; languages BCP 47
  • Successful responses are always wrapped: { "data": … , "meta": … }
GET /api/v1/ride-orders/9f3f6c1e-7b6a-4d9e-9d4c-0d1f3b8a2c11
Accept: application/json

200 OK
{
  "data": {
    "id": "9f3f6c1e-7b6a-4d9e-9d4c-0d1f3b8a2c11",
    "status": "in_progress",
    "vehicle_type": "taxi_urban",
    "fare": { "amount": 8850, "currency": "HKD" },
    "created_at": "2026-07-27T03:11:04.000Z",
    "accepted_at": "2026-07-27T03:11:41.000Z"
  }
}

Authentication

The v1 API accepts two schemes so devices can migrate without a simultaneous firmware release. New integrations should use scheme 1.

1. Bearer token (target scheme)

Authorization: Bearer <access_token>
X-JEK-Client: driver-pro        # or m1os | app | h5 | admin | web
X-JEK-Device-Sn: SN-000123      # device serial, required on the device surface

Tokens are obtained from the device sign-in endpoints and refreshed before expiry. Treat the token as a secret: never log it and never embed it in a URL.

2. Wonder-HMAC-SHA256 (legacy compatibility)

The existing signing interceptor shipped in the shared Flutter plugin keeps working unchanged. The server canonicalises the request, derives the signing key over three HMAC rounds and compares in constant time. Requests older than 300 seconds are rejected.

Authorization: Wonder-HMAC-SHA256 Credential=<appid>/<yyyyMMdd>/<service>,
               SignedHeaders=content-type;x-jek-device-sn;x-jek-timestamp,
               Signature=<hex>
X-JEK-Timestamp: 2026-07-27T03:11:04.000Z
X-JEK-Device-Sn: SN-000123

# canonical request
<METHOD>\n<path>\n<sorted query>\n<signed headers>\n<header names>\n<sha256(body)>

# key derivation
kDate    = HMAC("JEK" + secret_key, yyyyMMdd)
kService = HMAC(kDate, service)
kSigning = HMAC(kService, "jek_request")
signature = HMAC(kSigning, string_to_sign)

The HMAC scheme is a compatibility bridge only. It will be retired one release after Driver Pro and M1OS both ship bearer-token support; the retirement date will be announced here and in the changelog at least 90 days ahead.

Errors (RFC 9457)

Failures return application/problem+json. Branch on code, never on the human-readable title.

422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://developer.jek.app/problems/unprocessable_entity",
  "title": "Unprocessable entity",
  "status": 422,
  "code": "unprocessable_entity",
  "detail": "Pickup point is outside the service area",
  "errors": [
    { "field": "pickup.latitude", "code": "out_of_range", "message": "Outside Hong Kong coverage" }
  ]
}
  • invalid_request — 400, malformed payload
  • unauthorized — 401, missing / expired credentials
  • forbidden — 403, authenticated but not permitted
  • not_found — 404
  • conflict — 409, state machine conflict (e.g. order already accepted)
  • rate_limited — 429, honour Retry-After
  • upstream_error — 502, retry with backoff

Pagination

List endpoints are cursor paginated. Never construct cursors yourself — echo back what the previous response returned.

GET /api/v1/ride-orders?page[size]=25&page[cursor]=eyJvIjoiMjAyNi0wNy0yN1QwMzoxMToifQ

{
  "data": [ … ],
  "meta": { "next_cursor": "eyJvIjoiMjAyNi0wNy0yN1QwMjo1OToifQ", "page_size": 25 }
}

page[size] defaults to 25 and is capped at 100. next_cursor: null means the last page.

Migration switch

Behind each v1 path the platform can serve either the legacy backend or the new Supabase-native implementation. The URL, request and response contract never change — integrators do not ship a release when a route is switched over.

  • Every response carries X-JEK-Impl-Used: legacy | new. Log it; it is the fastest way to attribute a regression.
  • During validation both implementations run and the responses are compared field by field; the caller always receives the current implementation's answer, so shadow mode is invisible to clients.
  • Rollout is staged 0% → 5% → 50% → 100% and can be reverted instantly per endpoint, per client (driver-pro, m1os) or per device serial.
  • Internal accounts may force a side with X-JEK-Impl: legacy|new for debugging.

OpenAPI specification

The machine-readable spec is generated from the same catalog as this site, so it cannot drift. Import it into Postman, Insomnia or your codegen pipeline.