Both are sent to the Partner API host,
https://connect.laces.run/v1. A Laces
app sign-in (a Firebase ID token) is not a Partner API credential and is
rejected there with 401 invalid_token. The reverse holds too: a partner
credential sent to any other Laces host (for example api.laces.run) is
refused with 401 partner_credential_not_accepted.
Every key, token and id on this site is a made-up example value. Your real
keys are issued to you in the Laces app, under Settings → Developers.
Server side: OAuth 2.0 client credentials
POST /oauth/token implements the
OAuth 2.0 client credentials grant
(RFC 6749 §4.4).
You send your client id and secret, and get back a bearer token valid for one
hour.
The request
Authenticate either with HTTP Basic or with fields in the body. Use exactly one; sending both is400 invalid_request.
- Basic:
Authorization: Basic base64(client_id + ":" + client_secret). (Strictly, each part is URL-encoded first, but Laces ids and secrets contain only letters, digits,-and_, so encoding changes nothing.) - Body:
client_idandclient_secretfields.
grant_type=client_credentials is always required in the body. The body can
be application/x-www-form-urlencoded (the RFC’s format) or JSON.
scope is optional: a space-separated list to ask for fewer scopes than the
client holds. Leave it out to get all of them.
The response
200, in the bare OAuth shape (no Laces envelope), with
Cache-Control: no-store:
Caching the token
Tokens are opaque and valid for 3600 seconds. Cache the token and fetch a new one shortly before it expires, or when a call returns401 invalid_token.
Do not mint a token per API call: the token endpoint allows only about 10
requests a minute for each client id from one IP address
(rate limits).
Server-to-server only
Token endpoint errors
Errors use the OAuth shape (RFC 6749 §5.2), not the Laces envelope:Browser side: the publishable key
For code that runs in a visitor’s browser (hosted and embedded checkout), send the publishable key in a header:- Origin must match an allowed domain. The request’s
Originheader must exactly equal one of the key’s allowed domains. A request with noOrigin(curl, a server) is403 origin_not_allowed: a publishable key works only from a browser on your site. This guards against a copied key being used on someone else’s site. - It is capped to two scopes, whatever the client holds:
events:readandcheckout_sessions:write. It can list and read events and create a checkout session. Nothing else, so it can never create, pay or read orders. - CORS is answered only for origins on an allowlist.
Content-Type,Accept,Authorization,X-Laces-Publishable-KeyandIdempotency-Keyare the permitted request headers, and your script can read theRetry-AfterandX-Request-Idresponse headers. Cookies are not used.
Authentication errors on API routes
Every route other than the token endpoint answers with the Laces envelope and a stableerror_code. A 401 also
carries WWW-Authenticate: Bearer realm="laces-partner".
invalid_token, fetch a new token once and retry. If it fails again,
check that the key hasn’t been revoked and that you are calling
https://connect.laces.run/v1.
Changes to a key apply immediately: a revoke cuts off the key and its tokens
on the next request, and a removed permission stops working on the next
request. After Regenerate keys, tokens already issued keep working until
they expire (at most 1 hour); revoke the key to cut them off at once. A newly
added allowed domain can take up to 5 minutes to pass browsers’ CORS
preflight.