Skip to main content
Version 0.2Draft

Getting started with the Partner API

How authentication works: two custom headers

The Partner API does not use OAuth 2.0 or OpenID Connect (OIDC). Authentication is a pair of static headers that you receive when your partnership is provisioned:

HeaderValueNotes
X-Client-IdYour partner client identifierIdentifies which partner is calling. Safe to log.
X-Client-SecretYour partner client secretTreat as a credential: never log it, never embed it in client-side code, never commit it to a repo.

Every request must carry both headers. Calls without them are rejected.

caution

Treat X-Client-Secret as a credential: never log it, never embed it in client-side code, and never commit it to a repo.

How to obtain credentials

Your X-Client-Id and X-Client-Secret are issued during partner onboarding. If you do not yet have them, contact your LuxID Account manager - they are not self-service through the LuxID Console for the time being.

How to rotate the secret

If your secret is ever exposed (logged in error, leaked through a code commit, etc.), request a rotation from your LuxID Account manager. Until rotation completes, treat the exposed secret as compromised and assume an attacker could call the API in your name.

Base URL and environments

All environments use the same shape (host + /services + /luxid-partner-api); only the host changes. As a partner you integrate against UAT first, then Production.

EnvironmentBase URLAvailability
UAT (integration and testing)https://api-uat.luxid.lu/services/luxid-partner-apiPartner-facing
Productionhttps://api.luxid.lu/services/luxid-partner-apiPartner-facing

All endpoints below this base URL. For example, the "list applications" endpoint at path /applications is reached as:

GET https://api-uat.luxid.lu/services/luxid-partner-api/applications
caution

Only HTTPS is supported. Plain HTTP calls are rejected.

Request and response format

All requests and responses are JSON-encoded.

DirectionHeaderValue
Request bodyContent-Typeapplication/json
Response bodyAcceptapplication/json (also the default if you omit the header)

A complete first call

The smallest end-to-end call you can make is "list all applications managed by my partnership":

curl -sS \
-H "X-Client-Id: $LUXID_CLIENT_ID" \
-H "X-Client-Secret: $LUXID_CLIENT_SECRET" \
https://api-uat.luxid.lu/services/luxid-partner-api/applications

A successful response looks like:

{
"applications": [
{
"externalId": "app-abc-123",
"apiIdentifier": "my-client-id-in-oidc",
"name": "MyApp"
}
]
}

Two fields on each application matter, and they are easy to mix up:

  • externalId is the path identifier used throughout the Partner API. When other endpoints expect {applicationExtId} in the URL, this is what they mean.
  • apiIdentifier is the OIDC client_id that this application uses in the authentication layer. You will see it in the aud claim of ID tokens. It is not what the Partner API expects in path parameters.

If you confuse the two, you typically get a 404 with a Not found error.

The standard error shape

Most application-level errors come back as JSON with type and message:

{
"type": "NOT_FOUND",
"message": "Application not found: applicationExtId"
}

The HTTP status carries the broad category; type and message give the specific reason.

StatusMeaningTypical causes
400Functional errorMalformed request, invalid field, business-rule violation. Read message.
401UnauthorizedMissing or invalid X-Client-Id / X-Client-Secret. Returned by the API gateway (see shape below).
403ForbiddenYour credentials are valid, but the resource is not yours to access (wrong partner, wrong group, etc.).
404Not foundThe path identifier (applicationExtId, subscriberExtId, groupName, ...) does not match any resource.

Depending on which layer rejects the request, you may see two other shapes:

  • Authentication failures (API gateway). Missing or invalid credentials return HTTP 401 with a gateway body, not the type/message shape:

    {"httpCode": 401, "httpMessage": "Unauthorized", "moreInformation": "Invalid authentication."}

    A missing header reports "Missing X-Client-Id in header.". An unrecognised URL path returns HTTP 404 in the same gateway shape ("No resources match requested URI.").

  • Request-validation failures. A malformed query or path parameter can return an RFC 9457 (formerly RFC 7807) problem body:

    {"type": "about:blank", "title": "Bad Request", "status": 400, "detail": "Failed to convert 'from' with value: '...'", "instance": "/luxid-partner-api/events/search"}

Network-layer 5xx errors can happen and should be retried with exponential backoff.

Looking up users by email: MD5 of the lowercase address

Some endpoints take an account lookup parameter as {md5HashOfLowercaseEmail} rather than the raw email. Accounts are looked up by the MD5 hash of the lowercased email, computed with no leading or trailing whitespace.

Generate the hash with whatever your platform offers (md5sum on Linux, hashlib.md5(...).hexdigest() in Python, the crypto module in Node.js, etc.). MD5 is used here for compactness and case-insensitive lookup; it is not a security feature. The hash is enumerable and must be protected exactly like the email address itself - treat it as functionally equivalent to the email for privacy and security purposes, never expose it in logs or client-side code, and do not rely on it to obscure the underlying address.

Identifiers you will see

The Partner API uses several distinct identifiers. Knowing which one each endpoint expects saves a lot of confusion:

IdentifierWhat it isWhere it appears
applicationExtIdLuxID's stable id for one of your applicationsPath parameter on application, subscription, and group endpoints
applicationInstanceExtIdLuxID's stable id for one specific instance of an applicationInside the Application response under instances[]
apiIdentifierThe OIDC client_id of the applicationInside the Application response, also in OIDC token aud claim
subscriberExtIdLuxID's stable id for a user in the context of one of your applicationsPath parameter on subscription and claim endpoints
md5HashOfLowercaseEmailMD5 hash of the user's lowercased emailPath parameter on account-based lookup endpoints
groupNameA partner-managed group name (string)Path parameter on group endpoints

A user (LuxID Account) gets a subscriberExtId per partner application when they first subscribe to it. The same account therefore has a different subscriberExtId in two different applications you manage.

What is a "subscriber"?

A subscriber is a LuxID Account that has an active subscription to one of your applications. In practice, this happens the first time the user successfully signs in to your application through LuxID and consents to share their data. From that moment, LuxID tracks the relationship between this account and your application, gives it a stable subscriberExtId, and records a Subscription with the date the relationship began.

A subscriber stops being a subscriber when the subscription is deleted - either by the user from account.luxid.lu (opens in a new tab) under Applications, or by you through the Partner API's delete subscription endpoint.

Pagination

Endpoints that return potentially large collections expose page and pageSize query parameters and return a paginated envelope:

{
"page": 0,
"pageSize": 10,
"total": 142,
"items": [ ... ]
}

page is zero-based. pageSize defaults to 10 if omitted. The Event Hub endpoint is the main consumer of this pattern today (see Event Hub).

Rate limits

The Partner API has rate limits applied per X-Client-Id. If you exceed them, calls are rejected until the window resets. The specific limits are not published - they are not part of the OpenAPI document and are not communicated alongside your credentials, because they may be adjusted dynamically. Plan your back-end to back off and retry on 4xx/5xx responses with sensible jitter.

Where to go next

You now have everything you need to call any endpoint. Pick the capability page that matches what you want to do:

Updated 2026-06-05