API reference
API Overview
The Puntjes API is a RESTful JSON API. All endpoints are scoped to the authenticated vendor and require OAuth2 bearer tokens.
Base URL
https://puntjes.app/api/v1
All endpoints documented in this reference are relative to this base URL.
Request format
- Use
Content-Type: application/jsonfor request bodies - Include
Authorization: Bearer {token}on every request (see Authentication) - All monetary amounts are in cents (e.g.,
2500= EUR 25.00) - All point values are integers
Response envelope
All successful responses are wrapped in a data envelope:
{
"data": {
"id": 1,
"first_name": "Jan",
"last_name": "De Vries"
}
}
Paginated endpoints include pagination metadata:
{
"data": [...],
"links": {
"first": "...",
"last": "...",
"prev": null,
"next": "..."
},
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 15,
"total": 72
}
}
Follow links.next as given. It carries the query parameters you sent (filters,
sort order and per_page), so page 2 of a filtered result set is
the same filter as page 1. Rebuilding the URL yourself from
meta.current_page drops them: a walk that starts at per_page=5 silently
returns 15 rows on the second request.
Error responses
Errors follow a consistent format:
{
"error": {
"code": "CUSTOMER_NOT_FOUND",
"message": "No customer found with the given identifier.",
"status": 404,
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
Validation errors (422) include a details field:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid.",
"status": 422,
"request_id": "...",
"details": {
"email": ["The email field must be a valid email address."],
"identifiers.0.type": ["The identifier type must be one of: email, loyalty_card."]
}
}
}
See Error handling for a full list of error codes.
Idempotency
Every endpoint that moves points requires an idempotency_key (max 255
characters). Repeating a request with a key you already used returns the
original resource instead of processing it again, so these calls are safe to
retry after a network failure or timeout:
| Endpoint | Key is unique per | A replay returns |
|---|---|---|
POST /transactions | vendor | The original transaction; no points re-awarded |
POST /redemptions | vendor | The original redemption; no points deducted again, stock untouched |
POST /customers/{customer}/wallet/adjust | customer's wallet | The original ledger entry; balance not moved again |
Use a value that identifies the operation in your own system (an order ID, or a UUID generated once per checkout and reused across retries). Random values that change on every attempt defeat the purpose.
This protects against network issues and duplicate submissions from POS systems.
Endpoint summary
| Method | Endpoint | Description |
|---|---|---|
GET | /me | Get vendor branding |
GET | /customers/lookup | Look up a customer |
POST | /customers | Register a customer |
PUT PATCH | /customers/by-external-id/{externalId} | Update a customer |
POST | /customers/link-external-id | Link an external ID |
POST | /customers/by-external-id/{externalId}/send-card | Send the loyalty card, keyed on your own id |
POST | /customers/{customer}/send-card | Send the customer their loyalty card |
GET | /customers/{customer} | Get customer details |
POST | /transactions | Submit a transaction |
GET | /customers/{customer}/wallet | Get wallet balance |
GET | /customers/{customer}/ledger | List ledger entries |
GET | /customers/{customer}/wallet-pass | Download a wallet pass |
GET | /customers/{customer}/transactions | List customer transactions |
POST | /customers/{customer}/wallet/adjust | Adjust wallet balance |
GET | /campaigns | List campaigns |
GET | /rewards | List rewards |
POST | /redemptions | Redeem a reward |
GET | /redemptions/{code} | Look up a redemption |
POST | /redemptions/{code}/verify | Verify a redemption |
POST | /vouchers/{code}/verify | Verify a voucher |
GET | /products | List products |
POST | /products | Create a product |
POST | /products/batch | Bulk upsert products |
POST | /products/{externalId}/reward | Create a reward from a product |
GET | /products/{externalId} | Get a product |
PUT | /products/{externalId} | Create or update a product (upsert) |
PATCH | /products/{externalId} | Update a product |
DELETE | /products/{externalId} | Delete a product |
Get vendor branding
GET /api/v1/me
Return the branding of the vendor your token belongs to. Use it to colour a POS screen or a webshop widget the way the vendor's own customers already recognise, without hardcoding anything per client.
Example request
curl https://puntjes.app/api/v1/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response
{
"data": {
"display_name": "Koffiebar De Hoek",
"logo_url": "https://cdn.example.com/logo.png",
"brand_color_primary": "#4F46E5",
"brand_color_accent": "#06B6D4"
}
}