Handleidingen
Foutafhandeling
De Puntjes API gebruikt de standaard HTTP-statuscodes, en geeft bij elke fout een response met dezelfde opbouw terug.
Opbouw van een foutresponse
Elke fout komt in deze vorm terug:
{
"error": {
"code": "ERROR_CODE",
"message": "A human-readable description of what went wrong.",
"status": 422,
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"details": {}
}
}
| Veld | Beschrijving |
|---|---|
code | Machine-leesbare foutcode. Hierop laat je je integratie beslissen |
message | Foutmelding voor mensen. Geschikt voor je logs, niet voor je eindgebruikers |
status | HTTP-statuscode, dezelfde als die van de response zelf |
request_id | Uniek id van dit request, voor debuggen en support |
details | Extra context, aanwezig bij validatiefouten |
Dezelfde request_id komt terug in de responseheader X-Request-ID, ook bij een geslaagde response.
Elke logregel die het request bij ons schrijft, bevat dat id, zodat support het request op dat id
alleen terugvindt. Stuur je eigen X-Request-ID mee (letters, cijfers, ., _ en -, maximaal
128 tekens) en Puntjes gebruikt dat in plaats van er zelf een aan te maken.
HTTP-statuscodes
| Status | Beschrijving |
|---|---|
200 | Gelukt |
201 | Resource aangemaakt |
202 | Aanvaard. Het werk staat in een wachtrij en is nog niet uitgevoerd |
204 | Gelukt, en de response heeft geen body |
400 | Puntjes kon dit request aan geen enkele handelaar koppelen |
401 | Unauthorized. Ongeldig of verlopen token |
403 | Forbidden. Handelaar opgeschort of gedeactiveerd |
404 | Resource niet gevonden |
405 | Het pad bestaat wel, maar niet voor deze HTTP-methode |
409 | Conflict. De resource is al geregistreerd |
422 | Validatiefout of overtreding van een bedrijfsregel |
429 | Rate limit of planlimiet overschreden |
500 | Interne serverfout |
Overzicht foutcodes
Authenticatiefouten
| Code | Status | Beschrijving |
|---|---|---|
INVALID_CLIENT | 401 | Het token is geldig, maar de OAuth-client is ingetrokken of hoort bij geen enkele handelaar |
UNAUTHENTICATED | 401 | Het access token ontbreekt, is ongeldig of is verlopen |
Autorisatiefouten
| Code | Status | Beschrijving |
|---|---|---|
FORBIDDEN | 403 | Het token is geldig, maar mag deze actie niet uitvoeren |
SUBSCRIPTION_EXPIRED | 403 | Het abonnement van deze handelaar is afgelopen. Lezen antwoordt nog altijd 200; schrijven ligt stil tot de handelaar weer een plan heeft. Wachten lost dat niet op, en daarom is dit een 403 en geen 429. |
VENDOR_CONTEXT_MISSING | 500 | Het token wees naar geen enkele handelaar. Geef het opnieuw uit vanaf de API-client van de handelaar zelf |
VENDOR_CONTEXT_REQUIRED | 400 | Dit endpoint moet weten om welke handelaar het gaat, en het token zei dat niet |
VENDOR_DEACTIVATED | 403 | Het account van de handelaar is definitief gedeactiveerd |
VENDOR_INACTIVE | 403 | Het account van de handelaar staat niet in een status die API-verkeer aanvaardt |
VENDOR_PENDING | 403 | Het account van de handelaar is nog niet geactiveerd |
VENDOR_SUSPENDED | 403 | Het account van de handelaar is opgeschort en de respijtperiode is voorbij |
Validatiefouten
| Code | Status | Beschrijving |
|---|---|---|
BATCH_TOO_LARGE | 422 | Een batch mag hoogstens 100 producten bevatten. Splits je catalogus op en stuur hem in stukken |
VALIDATION_ERROR | 422 | De request body raakte niet door de validatie. In details staat per veld wat er misging |
Fouten bij klantregistratie
| Code | Status | Beschrijving |
|---|---|---|
CUSTOMER_ALREADY_LINKED | 409 | De klant heeft al een andere external_id. Koppelen overschrijft nooit wat er al staat |
EXTERNAL_ID_DUPLICATE | 409 | Deze external_id staat al bij deze handelaar geregistreerd |
IDENTIFIER_DUPLICATE | 409 | Deze identifier staat al bij deze handelaar geregistreerd, of een andere klant claimde de gescande kaart net eerder |
LOYALTY_CARD_NOT_FOUND | 422 | De loyalty_card die je meestuurt is bij deze handelaar geen vrije kaart. Scan opnieuw, of laat identifiers weg en laat Puntjes zelf een kaart uitgeven. |
Resourcefouten
| Code | Status | Beschrijving |
|---|---|---|
BRANCH_INACTIVE | 422 | De vestiging is gedeactiveerd. Je krijgt deze code bij een nieuwe transactie, en bij een credential waarvan de standaardvestiging intussen sloot. Puntjes gaat nooit stilzwijgend verder zonder vestiging |
BRANCH_NOT_FOUND | 422 | Geen enkele vestiging van jou heeft deze branch-sleutel. Puntjes kijkt ook bij je gedeactiveerde vestigingen, dus je ziet het verschil tussen een typfout en een winkel die dicht is |
CUSTOMER_DEACTIVATED | 422 | De klant bestaat, maar is gedeactiveerd, dus hij kan geen punten sparen of uitgeven |
CUSTOMER_HAS_NO_EMAIL | 422 | Van deze klant is geen e-mailadres bekend, dus zijn klantenkaart kan nergens naartoe |
CUSTOMER_NOT_FOUND | 404 | Geen klant gevonden met deze identifier of dit ID |
EXTERNAL_ID_NOT_FOUND | 404 | Geen enkele klant van jou heeft deze external_id. Verwijderde en geanonimiseerde klanten bereik je niet meer. Een gedeactiveerde klant vind je wel, en die krijgt CUSTOMER_DEACTIVATED |
NO_WALLET | 422 | De klant heeft bij deze handelaar nog geen wallet, dus er is geen saldo om van af te schrijven |
VOUCHER_NOT_FOUND | 404 | Geen enkele bon van jou heeft deze BON--code. Een code van een andere handelaar geeft dezelfde 404. |
PRODUCT_EXTERNAL_ID_DUPLICATE | 409 | Er bestaat al een product met deze external_id. Gebruik PUT: die maakt aan of werkt bij |
PRODUCT_NOT_FOUND | 404 | Geen enkel product van jou heeft deze external_id |
REDEMPTION_NOT_FOUND | 404 | Geen inwisseling gevonden met deze bevestigingscode |
REWARD_NOT_FOUND | 404 | Geen enkele beloning van jou heeft dit id |
REWARD_UNAVAILABLE | 422 | De beloning is inactief, valt buiten de periode waarin ze beschikbaar is, of is niet gevonden |
WALLET_NOT_FOUND | 404 | Deze klant heeft geen wallet, dus er valt niets te lezen of aan te passen |
Bedrijfsregelfouten
| Code | Status | Beschrijving |
|---|---|---|
BRANCH_REQUIRED | 422 | Deze beloning of campagnebon geldt maar in een deel van je vestigingen, en je request noemde er geen enkele van: ofwel helemaal geen branch, ofwel een die niet in die lijst staat. Er wordt niets verzilverd, dus dezelfde aanroep slaagt zodra je een toegelaten vestiging meestuurt |
CODE_ALREADY_USED | 422 | Deze inwisseling is al geverifieerd |
CODE_EXPIRED | 422 | De bevestigingscode is verlopen |
IDEMPOTENCY_KEY_CONFLICT | 422 | Deze idempotency_key is al gebruikt, voor een andere klant of een andere beloning |
INSUFFICIENT_BALANCE | 422 | De klant heeft niet genoeg punten voor deze inwisseling of wallet-aanpassing |
OUT_OF_STOCK | 422 | Deze beloning is niet meer op voorraad |
VERIFICATION_FAILED | 422 | De code raakte niet geverifieerd, en niet omdat hij verlopen of al gebruikt is |
VOUCHER_ALREADY_USED | 422 | De bon is al verbruikt. Verifiëren is precies wat hem verbruikt, dus een tweede aanroep komt hier uit. Vang dat op voordat je uit coulance nog een korting geeft. |
VOUCHER_EXPIRED | 422 | De bon is voorbij zijn valid_until-datum, en die dag telt zelf nog mee. De laatste geldige dag is dus nog niet "voorbij" |
Rate limiting
| Code | Status | Beschrijving |
|---|---|---|
CARD_SEND_THROTTLED | 429 | De klant kreeg zijn klantenkaart net al opgestuurd. Anders dan de twee andere 429-codes verdwijnt deze binnen enkele seconden. details.retry_after zegt na hoeveel. |
PLAN_LIMIT_EXCEEDED | 429 | Het abonnement van de handelaar heeft zijn transactietegoed voor deze periode opgebruikt |
RATE_LIMITED | 429 | Te veel requests in dit venster. Wacht even en probeer opnieuw |
Requestfouten
| Code | Status | Beschrijving |
|---|---|---|
METHOD_NOT_ALLOWED | 405 | Het pad bestaat wel, maar niet voor deze HTTP-methode |
ROUTE_NOT_FOUND | 404 | Op dit pad staat geen endpoint. Controleer de versieprefix en de spelling |
Serverfouten
| Code | Status | Beschrijving |
|---|---|---|
INTERNAL_ERROR | 500 | Er liep iets mis aan onze kant. Probeer opnieuw, en vermeld request_id als het blijft gebeuren |
Afkomstig van POST /api/v1/customers. Het volledige contract van dat request staat bij
Klant-endpoints.
De identifiers-array accepteert twee types
identifiers[].type aanvaardt precies twee waarden: email en loyalty_card. Elke andere
waarde krijgt een VALIDATION_ERROR (422) terug, en de melding noemt de types die wel mogen,
zodat je het zelf kunt oplossen. Stuurt je client een type dat er niet meer bij hoort, dan pas
je die aan. Puntjes vangt oude waarden nergens op.
Lezen verandert niet: GET /customers/lookup zoekt alleen op de waarde zelf, dus elke
identifier die je klanten al dragen, blijft werken.
Aan vier codes zie je of opnieuw proberen zin heeft, en maar drie daarvan zijn een 429. Kijk
dus naar de code en niet naar de status: sinds de vierde in een andere statusklasse
binnenkwam, houdt de status deze gevallen niet meer uit elkaar.
RATE_LIMITED gaat vanzelf over: even wachten en opnieuw proberen. CARD_SEND_THROTTLED
gaat ook vanzelf over, binnen enkele seconden, en begrenst de mailbox van één klant in
plaats van jouw doorvoer. details.retry_after zegt na hoeveel seconden, en de
Retry-After-header draagt hetzelfde getal. PLAN_LIMIT_EXCEEDED gaat binnen de periode
niet over: het transactietegoed van het abonnement is voor deze periode op, en opnieuw
proberen verbruikt alleen wat er nog rest. De volgende periode vult het weer aan, en een
groter abonnement legt de grens hoger.
SUBSCRIPTION_EXPIRED is een 403, geen vierde 429. Schrijven stopt zodra het abonnement
van een handelaar afloopt, terwijl lezen gewoon 200 blijft antwoorden, dus een GET krijgt
deze code nooit te zien. Wachten lost het niet op: de handelaar blijft alleen-lezen tot die
weer een plan heeft, en iemand moet dat plan kiezen. Een beheerder van de handelaar doet dat
zelf onder Facturatie; een platformbeheerder kan er ook een namens de handelaar toewijzen.
De code hoort bij dezelfde 403-familie als VENDOR_SUSPENDED en VENDOR_DEACTIVATED, met
één verschil waar je rekening mee moet houden: die twee weigeren ook leesacties.
Details bij een validatiefout
Bij de foutcode VALIDATION_ERROR staat in het veld details per veld wat er misging:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid.",
"status": 422,
"request_id": "...",
"details": {
"identifiers.0.type": ["The identifier type must be one of: email, loyalty_card."],
"email": ["The email has already been taken."]
}
}
}
Elke sleutel in details is een veldnaam, en de waarde is een array met de foutmeldingen voor dat veld.
Fouten afhandelen in je integratie
async function submitTransaction(data) {
const response = await fetch('/api/v1/transactions', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
const { error } = await response.json();
switch (error.code) {
case 'CUSTOMER_NOT_FOUND':
// Vraag de gebruiker om de klant te registreren
break;
case 'PLAN_LIMIT_EXCEEDED':
// Toon limietwaarschuwing, neem contact op met de leveranciersbeheerder
break;
case 'VALIDATION_ERROR':
// Verwerk fouten per veld uit error.details
break;
default:
// Log error.request_id voor ondersteuning
console.error(`API error: ${error.code}`, error.request_id);
}
}
return response.json();
}