Introduction
Authentication
The Puntjes API uses OAuth2 with the client credentials grant for authentication. This is a machine-to-machine flow designed for POS systems, backend services, and integrations that act on behalf of a vendor.
Obtaining credentials
API clients are created and managed through the Puntjes admin portal. Navigate to API Clients in the sidebar to:
- Create a new API client with a label
- View your client ID
- Reveal or regenerate your client secret
- Revoke compromised clients
Each OAuth client is bound to a specific vendor. When you authenticate with a client's credentials, the API scopes all operations to that vendor automatically.
Requesting an access token
Exchange your client credentials for an access token by sending a POST request to the OAuth token endpoint:
curl -X POST https://puntjes.app/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=client_credentials \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET
Response
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJS..."
}
| Field | Description |
|---|---|
token_type | Always Bearer |
expires_in | Token lifetime in seconds (default: 3600 = 60 minutes) |
access_token | The JWT token to include in API requests |
Using the token
Include the access token in the Authorization header of every API request:
curl https://puntjes.app/api/v1/customers/lookup?identifier=CARD-001 \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJS..."
Token expiration
Access tokens expire after 60 minutes. When a token expires, the API returns a 401 Unauthorized response. Your integration should:
- Catch
401responses - Request a new access token
- Retry the original request
Token caching
Request a new token only when needed. Cache the token and reuse it until it expires or you receive a 401 response.
Avoid requesting a new token for every API call.
Vendor scoping
Authentication implicitly determines which vendor's data you access. The OAuth client is linked to a vendor, so:
- All customers, transactions, rewards, and campaigns returned are scoped to your vendor
- You cannot access another vendor's data
- Creating resources (customers, transactions) automatically associates them with your vendor
Vendor status requirements
Your vendor account must be in active status to use the API. If the vendor is:
- Suspended: API access continues during the grace period (configured per vendor), then returns
403 VENDOR_SUSPENDED - Deactivated: API access is immediately blocked with
403 VENDOR_DEACTIVATED - Pending: API access is not available until the vendor is activated
A separate check sits beside these. When a paid period has lapsed and the workspace's
subscription has ended, reads keep answering 200 and every write answers
403 SUBSCRIPTION_EXPIRED. Retrying does not help: the route back is a person choosing a plan. A
vendor admin does that under Billing, and that is
why this is a 403 rather than one of the 429s that clear on their own.
Security best practices
- Store client secrets securely: never expose them in frontend code or version control
- Use HTTPS for all API communication
- Rotate client secrets periodically via the admin portal's Regenerate Secret feature
- Revoke compromised clients immediately
- Give each POS terminal or integration its own API client, so you can tell which system did what