Before you begin
Create a Customer managed integration, complete credential enrollment, and wait for
administrator approval. Keep the returned subject value and the enrolled RSA
private key available to the service that requests tokens.
This procedure creates two separate values. Your service first creates a
client assertion, which is a signed JWT that proves possession of the
private key. The authorization server validates that assertion and returns an
access token, which authorizes Customer API requests for 10 minutes. Never
send the client assertion in an API Authorization header.
Create the client assertion
A JWT has a header, a claims object, and a signature. Create its header with these values:
| Header | Required value | Meaning |
|---|---|---|
alg |
RS256 |
Sign with RSA and SHA-256. |
kid |
The enrolled public JWK thumbprint | Identifies the public key Wavac should use to verify the signature. |
Do not include jku, x5u, or x5c. Wavac resolves only the public key that
was reviewed during enrollment and doesn't retrieve signing material from a URL
supplied by the assertion.
Set the JWT claims as follows. Numeric times are Unix timestamps in whole seconds, not RFC 3339 strings.
| Claim | Required value | Meaning |
|---|---|---|
iss |
The credential subject returned by enrollment |
Identifies who created the assertion. |
sub |
The same credential subject |
Identifies the integration credential being authenticated. |
aud |
https://api.wavac.io/api/customer/oauth/token |
Restricts the assertion to the Wavac token endpoint. |
iat |
Current time | Records when the assertion was created. |
nbf |
Current time, allowing only necessary clock tolerance | Prevents use before the intended time. |
exp |
No more than five minutes after iat |
Ends the assertion's short acceptance window. |
jti |
A new, unpredictable value no longer than 128 characters | Gives this assertion a unique identity so reuse can be rejected. |
Sign the encoded header and claims with the private key that matches the
enrolled public JWK. Keep system time synchronized. A large clock difference can
place iat, nbf, or exp outside the accepted window.
Exchange the assertion
Write the compact, signed assertion to a private mode-0600 file. This avoids
placing it directly in shell history or a process argument:
curl --fail-with-body https://api.wavac.io/api/customer/oauth/token \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode client_assertion@client-assertion.jwt \
--data-urlencode 'scope=customer.discovery.read customer.data.read'
grant_type=client_credentials says that the application is acting as itself,
without an interactive user. client_assertion_type identifies the OAuth JWT
assertion profile. scope is a space-separated request for specific
permissions; every requested scope must already be allowed for the credential.
Delete client-assertion.jwt after the exchange. The assertion is single-use,
so reusing the same jti is rejected even if exp has not passed.
Read the token response
A successful response contains these fields:
{
"access_token": "<access-token>",
"token_type": "Bearer",
"expires_in": 600,
"scope": "customer.discovery.read customer.data.read"
}
expires_in is the lifetime in seconds from issuance. Cache the access token in
memory and replace it shortly before expiry. The Customer API does not issue a
refresh token for this flow; create a fresh assertion and repeat the exchange
when another access token is needed.
Token requests are separately rate limited across the integration. Reuse a valid token for its approved scope set and coordinate scheduled jobs and manual tests; starting another process does not provide another allowance. See authentication limits before choosing refresh frequency or retry behavior.
Verify the token
Send the access token, not the assertion, to discovery:
GET /api/customer/v1/discovery
Host: api.wavac.io
Authorization: Bearer <access-token>
Accept: application/json
A 200 OK response confirms that token authentication succeeded and shows the
resources currently authorized for the integration. Continue with use an
access token for storage, renewal, and 401
recovery guidance.
An invalid assertion produces a generic invalid_client response. A temporary
authorization-server failure produces temporarily_unavailable; wait with
bounded backoff, create a fresh assertion, and retry. The assertion format
follows the OAuth JWT assertion profile (RFC
7523), and its claim names come
from JSON Web Token (RFC
7519).