Issue the package
- Open Dashboard Customer API settings and select the intended integration.
- Confirm its site, line, scopes, and authentication status before requesting a package.
- Generate and download the Postman package. The environment contains a Wavac-signed JWT marked as a secret.
- Import both JSON files, select the imported environment, and run discovery.
The JWT is valid for up to seven days. Generating a replacement package revokes the previous package JWT for that integration.
Use the JWT outside Postman
Read the JWT from a protected environment variable or secret file. Do not pass it on the command line as a literal value.
Linux: Bash and curl
# Prompt without echoing the JWT or placing it in shell history.
read -rsp 'Customer API JWT: ' IOTSNAP_CUSTOMER_API_JWT
echo
export IOTSNAP_CUSTOMER_API_JWT
# Send the JWT in the Authorization header, never in the URL.
curl --fail-with-body \
-H "Authorization: Bearer $IOTSNAP_CUSTOMER_API_JWT" \
https://api.wavac.io/api/customer/v1/discovery
# Remove the JWT from this shell when the tutorial is complete.
unset IOTSNAP_CUSTOMER_API_JWT
Windows: PowerShell
# Prompt without echoing the JWT or placing it in command history.
$secureToken = Read-Host 'Customer API JWT' -AsSecureString
$env:IOTSNAP_CUSTOMER_API_JWT = `
[System.Net.NetworkCredential]::new('', $secureToken).Password
# Send the JWT in the Authorization header, never in the URL.
Invoke-RestMethod -Uri 'https://api.wavac.io/api/customer/v1/discovery' `
-Headers @{ Authorization = "Bearer $env:IOTSNAP_CUSTOMER_API_JWT" }
# Remove the JWT from this process when the tutorial is complete.
$env:IOTSNAP_CUSTOMER_API_JWT = $null
$secureToken.Dispose()
Runnable .NET example
The C# client is cross-platform. Set the JWT with the Linux or Windows instructions below, then place this commented example in Program.cs.
// Import the standard HTTP authentication header type.
using System.Net.Http.Headers;
// Read the JWT from process configuration instead of placing it in source.
var token = Environment.GetEnvironmentVariable("IOTSNAP_CUSTOMER_API_JWT")
?? throw new InvalidOperationException("Set IOTSNAP_CUSTOMER_API_JWT.");
// Create a client for the public Customer API and attach the bearer token.
using var client = new HttpClient {
BaseAddress = new Uri("https://api.wavac.io")
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// Call discovery first, print the response for learning, and fail on rejection.
using var response = await client.GetAsync("/api/customer/v1/discovery");
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
response.EnsureSuccessStatusCode();
Linux: Run with Bash
# Create the console project once and replace Program.cs with the example above.
dotnet new console --name CustomerApiJwt
cd CustomerApiJwt
# Prompt for the JWT, expose it only to this process tree, and run the tutorial.
read -rsp 'Customer API JWT: ' IOTSNAP_CUSTOMER_API_JWT && echo
export IOTSNAP_CUSTOMER_API_JWT
dotnet run
unset IOTSNAP_CUSTOMER_API_JWT
Windows: Run with PowerShell
# Create the console project once and replace Program.cs with the example above.
dotnet new console --name CustomerApiJwt
Set-Location CustomerApiJwt
# Prompt for the JWT, expose it only to this process tree, and run the tutorial.
$token = Read-Host 'Customer API JWT' -AsSecureString
$env:IOTSNAP_CUSTOMER_API_JWT = [System.Net.NetworkCredential]::new('', $token).Password
dotnet run
# Clear the process-local value after the tutorial exits.
$env:IOTSNAP_CUSTOMER_API_JWT = $null
$token.Dispose()
Runnable Python example
This client reads the JWT from process configuration and calls discovery using only Python's standard library. Save it as customer_api_jwt.py.
# Import only Python standard-library modules; no package installation is required.
import os
import urllib.request
# Read the JWT from process configuration instead of placing it in source.
token = os.environ.get("IOTSNAP_CUSTOMER_API_JWT")
if not token:
raise RuntimeError("Set IOTSNAP_CUSTOMER_API_JWT.")
# Send the JWT in the Authorization header, never in the URL.
request = urllib.request.Request(
"https://api.wavac.io/api/customer/v1/discovery",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/json",
},
)
# Call discovery first and require a successful HTTP response.
with urllib.request.urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
Linux: Run Python with Bash
# Prompt for the JWT without echoing it, run the client, and then clear it.
read -rsp 'Customer API JWT: ' IOTSNAP_CUSTOMER_API_JWT && echo
export IOTSNAP_CUSTOMER_API_JWT
python3 customer_api_jwt.py
unset IOTSNAP_CUSTOMER_API_JWT
Windows: Run Python with PowerShell
# Prompt for the JWT without echoing it and expose it only to this process tree.
$token = Read-Host 'Customer API JWT' -AsSecureString
$env:IOTSNAP_CUSTOMER_API_JWT = [System.Net.NetworkCredential]::new('', $token).Password
# Run the client, then clear the process-local value.
py customer_api_jwt.py
$env:IOTSNAP_CUSTOMER_API_JWT = $null
$token.Dispose()
Register a customer-signed JWT
- Create an RSA signing key of at least 3072 bits and a currently valid X.509 public certificate that permits digital signatures. Keep the private key in your secret manager or hardware security module.
- In the integration's Authentication setup, choose Customer-signed JWT.
- Enter the exact
issandsubvalues your client will send, then paste only the PEM public certificate. - Sign tokens with
RS256. Include exactly one base64 DER leaf certificate in the protectedx5cheader, the Customer API audience supplied by Wavac inaud, an expiration inexp, and the granted scopes inscope.
This is commented JSON for learning. Remove every comment before serializing and signing the JWT.
// Protected JWT header: remove comments before encoding this JSON.
{
// RS256 is the only supported signing algorithm for customer-owned keys.
"alg": "RS256",
// typ identifies this compact signed value as a JWT.
"typ": "JWT",
// x5c contains one base64 DER public leaf certificate, not a private key.
"x5c": ["base64-DER-public-certificate"]
}
// JWT payload: use the values registered for this integration.
{
// iss and sub must exactly match the registered customer-signed method.
"iss": "https://customer.example",
"sub": "production-export",
// Use the audience and least-privilege scopes supplied by Wavac.
"aud": "customer-api",
"scope": "customer.discovery.read customer.data.read",
// iat and exp are NumericDate values; keep the token lifetime short.
"iat": 1788278400,
"exp": 1788278700
}
The
iss,sub, certificate, and audience must match the registered method. Never place the private key or a private-key PEM in Dashboard.
Validation and renewal
- The API validates the RS256 signature, certificate validity and key strength, issuer, subject, audience, token lifetime, registered credential identity, integration state, scopes, and customer policy.
- Wavac-issued package tokens remain valid for at most seven days. Do not extend, edit, or re-sign them.
- Rotate a customer-signed method by supplying the replacement public certificate and its issuer and subject. Rotation immediately invalidates tokens tied to the previous values.
- Replace credentials atomically in the client secret store. If a package or private key is exposed, rotate immediately and remove exposed copies from local and CI artifacts.