Customer API / Authentication

Request a token with a customer-managed assertion

Use this optional mode when your organization owns the JWT signer; platform-managed integrations authenticate with mTLS instead.

In this article

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).