LightUp Third-Party API — English summary
⚠️ The API endpoint is not open yet
api.lightup.techdoes not accept requests today. This documentation describes a settled contract — build against it and verify against the bundled mock server — but there is no live environment to call. The activation date will be announced separately, and this page and the changelog will be updated at the same time.
The full documentation is written in Traditional Chinese. This page is the English summary: enough to decide whether the API fits, and enough to read the machine-readable contract on your own. For anything that matters operationally, read the Chinese page it links to — that is the normative text.
Contract status: Proposed (draft). None of the endpoints described here exist yet.
Every capability carries an
availability: planned, preview or generally available. All five v1 capabilities are preview — the contract is settled and you can build against it and verify against the bundled mock server, but the implementation and provisioning are not done, and no environment is guaranteed to answer. The addresses are decided (api.lightup.techfor the API,developers.lightup.techfor this site) — which is not the same as the endpoint being open. See the notice at the top of the home page.
What this API is
An API for customer applications that run outside the LightUp platform. LightUp's own products (app, dashboard, storefront) use their own BFFs and are not in scope here.
Base URL: https://api.lightup.tech/v1, decided on 2026-09-23. Two lookalike
hosts are not this API and will not accept these calls: api.app.lightup.tech is the
first-party app BFF and api.d.lightup.tech is the dashboard BFF. auth.lightup.tech
is part of this contract, but it is the login and token endpoint, not the API host.
A decided host does not mean a live endpoint: the gateway is not deployed and every
capability is still preview.
Authorization modes (v1)
| Mode | Grant | Token profile | Subject | TTL | Refresh |
|---|---|---|---|---|---|
| Machine / background | client_credentials |
workload |
service account (acc_…) |
15 min | none |
| End user, acting on their own data | authorization_code + PKCE (S256) |
self |
person (psn_…) |
15 min | yes, strict rotation |
There is no delegation in v1. Acting on behalf of another person (the delegated
profile) is v2 planning material. Sending your own user_id or x-user-* header is not
proof of delegation and the platform will not accept it.
Token validation
Every request is checked twice: locally, then against the platform's authorization service.
- ES256, keys from the fixed JWKS at
https://auth.lightup.tech/.well-known/jwks.json. The platform never fetches keys from a URL a token names. audislightup-api— not the client id.- JOSE header
typmust beat+jwt. An ID token is not an access token and is rejected here. - Then, on every request, the gateway asks the authorization service whether this client, this account, this grant, this plan and this resource still allow the call. There is no allow-cache in v1.
- If authorization cannot be determined, the gateway returns 503, never a signature-only pass. Fail-closed.
Capabilities
Scopes are named lightup.<capability>. The effective set is an intersection:
effective = tenant plan ∩ tenant's grant to this app ∩ account/user consent ∩ resource policy
Adding a capability to the catalogue does not widen an existing grant. Upgrading a plan does not widen an existing grant either.
| Capability | Resource | Min plan | Availability | What it does |
|---|---|---|---|---|
lightup.profile.self.read |
self | L1 | preview | 讀這張 token 代表的 service account |
lightup.person.read |
person | L2 | preview | 讀獲授權人員的最少欄位 |
lightup.person.metadata.read |
person | L2 | preview | 讀本 app 自己的人員 metadata |
lightup.person.metadata.write |
person | L2 | preview | 寫/刪本 app 自己的人員 metadata |
lightup.tenant.info.read |
tenant | L1 | preview | 讀租戶公開識別欄位 |
Revocation
Revocation takes effect on the next request. No numeric propagation bound is published; it will be measured on a real isolated deployment before any number is promised. Token TTL is a ceiling, not a revocation mechanism.
Errors
Errors are application/problem+json with a stable machine-readable error code, a
human message that may change wording at any time, and a requestId.
Every response — success or failure — carries x-request-id. Include it in any support
request; it is also the idempotency key the platform meters on.
The two codes worth internalising: 403 means no, retrying will not help. 503 means the platform cannot decide right now, retry with exponential backoff.
| Status | error codes |
|---|---|
| 401 | invalid_token |
| 403 | client_unknown, client_disabled, actor_unknown, tenant_suspended, client_not_third_party, profile_not_supported, subject_revoked, epoch_changed, capability_not_granted, plan_capability_excluded, capability_not_in_token, capability_unavailable, resource_type_mismatch, tenant_mismatch, no_relationship, forbidden |
| 404 | not_found |
| 409 | revision_conflict, already_exists |
| 413 | payload_too_large |
| 422 | quota_exceeded, schema_invalid |
| 428 | precondition_required |
| 429 | rate_limited |
| 503 | authorization_unavailable, token_verification_unavailable |
The 403 codes are the gateway's stable deny reasons. Two of them — tenant_mismatch
and no_relationship — come back as 404 on a person resource, so that the
difference between 403 and 404 is not itself a probe for whether a person exists.
forbidden is the catch-all for a deny reason this version of the gateway does not
recognise: still a denial, never a pass and never a retryable 503. What each code means
and how to fix it is in the API reference, generated from the contract.
Metadata
A small amount of structured data can be attached to a person, addressed by
(tenant, person, extension). The tenant comes from the token, never from a parameter.
Every write must state its precondition, with plain RFC 9110 semantics: create with
If-None-Match: *, update with If-Match: "<etag>". There is no unconditional
overwrite — neither header, or both, is 428 precondition_required. Every conflict
is 409, never 412: already_exists when you tried to create something that is there,
revision_conflict when the version moved under you. Both write nothing.
| Limit | Value | On exceed |
|---|---|---|
| 單筆 data 大小 | 16 KiB | 413 |
| 每 tenant 每 extension 筆數 | 50,000 | 422 quota_exceeded |
| 每 tenant extension 數 | 20 | 422 quota_exceeded |
| 分頁每頁筆數 | 預設 20,上限 100 | 422 |
Quota exhaustion never deletes data: writes are refused, reads, deletes and export stay available. Do not store photos, per-question event streams, unbounded lists, or anything that is treated as a permission, a balance, an entitlement or an identity binding.
Compatibility
/v1 in the path. Added fields do not bump the version — tolerate unknown fields.
Removing a field or changing its meaning bumps to /v2. Deprecations are announced at
least 90 days ahead, through the changelog on this site and in the offline bundle.
Offline bundle
The website and the versioned offline bundle are produced by the same build from the same
sources. The bundle carries the OpenAPI document, extracted JSON schemas, all guides,
runnable examples with a mock server, llms.txt / llms-full.txt for AI tooling, the
changelog, and a manifest.json with the contract version, source commit and a sha256 for
every file.
Support
Include the x-request-id, the timestamp, the operation, your client id, the status and
error code, and the contract version. Never include tokens, secrets or real customer
data. If a credential was exposed, ask for revocation first, then rotate.