API-referentie

API-overzicht

De Puntjes API is een RESTful JSON API. Elk endpoint verwacht een OAuth2 bearer token en geeft alleen de gegevens van de handelaar waarvoor dat token is uitgegeven.


Basis-URL

https://puntjes.app/api/v1

Alle endpoints in deze referentie staan onder deze basis-URL.


Requestformaat

  • Gebruik Content-Type: application/json voor request bodies
  • Stuur Authorization: Bearer {token} mee bij elk request (zie Authenticatie)
  • Alle geldbedragen zijn in eurocenten (bv. 2500 = EUR 25,00)
  • Alle puntwaarden zijn gehele getallen

Response-envelope

Elke geslaagde response zit in een data-envelope:

{
    "data": {
        "id": 1,
        "first_name": "Jan",
        "last_name": "De Vries"
    }
}

Gepagineerde endpoints zetten de hele pagina binnen die envelope, dus de items staan twee niveaus diep, in data.data, naast data.links en data.meta. GET /rewards is de enige lijst zonder pagina's: daar is data meteen de array.

{
  "data": {
    "data": [...],
    "links": {
      "first": "...",
      "last": "...",
      "prev": null,
      "next": "..."
    },
    "meta": {
      "current_page": 1,
      "last_page": 5,
      "per_page": 15,
      "total": 72
    }
  }
}

Volg data.links.next zoals je die krijgt. Die bevat de queryparameters die je zelf hebt meegestuurd (filters, sorteervolgorde en per_page), dus pagina 2 van een gefilterde lijst heeft hetzelfde filter als pagina 1. Bouw je de URL zelf op uit data.meta.current_page, dan raak je ze kwijt: een doorloop die met per_page=5 start, krijgt bij het tweede request stilzwijgend 15 rijen terug.


Foutresponses

Elke fout heeft dezelfde vorm:

{
    "error": {
        "code": "CUSTOMER_NOT_FOUND",
        "message": "No customer found with the given identifier.",
        "status": 404,
        "request_id": "550e8400-e29b-41d4-a716-446655440000"
    }
}

Validatiefouten (422) bevatten een details-veld:

{
    "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."]
        }
    }
}

Zie Foutafhandeling voor de volledige lijst met foutcodes.


Idempotentie

Elk endpoint dat punten verplaatst vereist een idempotency_key (max. 255 tekens). Stuur je een request opnieuw met een sleutel die je al gebruikt hebt, dan krijg je de oorspronkelijke resource terug en wordt er niets een tweede keer verwerkt. Deze aanroepen zijn dus veilig te herhalen na een netwerkfout of een time-out:

EndpointSleutel uniek perWat een herhaling teruggeeft
POST /transactionshandelaarDe oorspronkelijke transactie; geen punten opnieuw toegekend
POST /redemptionshandelaarDe oorspronkelijke verzilvering; geen punten opnieuw afgeschreven, voorraad ongewijzigd
POST /customers/{customer}/wallet/adjustwallet van de klantDe oorspronkelijke grootboekregel; saldo niet opnieuw verplaatst

Gebruik een waarde die de handeling in jouw eigen systeem aanwijst (een order-ID, of een UUID die je één keer per afrekening genereert en bij elke retry hergebruikt). Willekeurige waarden die bij elke poging veranderen maken idempotentie zinloos.

Dat vangt netwerkproblemen op, en kassasystemen die hetzelfde request twee keer versturen.


Overzicht van endpoints

MethodeEndpointBeschrijving
GET/meBranding van de handelaar ophalen
GET/customers/lookupEen klant opzoeken
POST/customersEen klant registreren
PUT PATCH/customers/by-external-id/{externalId}Een klant bijwerken
POST/customers/link-external-idEen external ID koppelen
POST/customers/by-external-id/{externalId}/send-cardDe klantenkaart versturen via je eigen id
POST/customers/{customer}/send-cardDe klantenkaart naar de klant sturen
GET/customers/{customer}Klantgegevens ophalen
POST/transactionsEen transactie indienen
GET/customers/{customer}/walletWallet-saldo ophalen
GET/customers/{customer}/ledgerGrootboekregels opvragen
GET/customers/{customer}/redemptionsDe verzilveringen van een klant opvragen
GET/customers/{customer}/vouchersDe open bonnen van een klant opvragen
GET/customers/{customer}/wallet-passEen wallet-pas downloaden
GET/customers/{customer}/transactionsKlanttransacties weergeven
POST/customers/{customer}/wallet/adjustWallet-saldo aanpassen
GET/campaignsCampagnes weergeven
GET/branchesActieve vestigingen ophalen
GET/rewardsBeloningen weergeven
POST/redemptionsEen beloning verzilveren
GET/redemptions/{code}Een verzilvering opzoeken
POST/redemptions/{code}/verifyEen verzilvering verifiëren
GET/vouchers/{code}Een bon opzoeken
POST/vouchers/{code}/verifyEen bon verifiëren
GET/productsProducten weergeven
POST/productsEen product aanmaken
POST/products/batchProducten in bulk upserten
POST/products/{externalId}/rewardEen beloning maken van een product
GET/products/{externalId}Een product ophalen
PUT/products/{externalId}Een product aanmaken of bijwerken (upsert)
PATCH/products/{externalId}Een product bijwerken
DELETE/products/{externalId}Een product verwijderen

Branding van de handelaar ophalen

GET /api/v1/me

Geeft de branding terug van de handelaar waar je token bij hoort. Kleur er een kassascherm of een webshopwidget mee zoals de klanten van die handelaar het al kennen, zonder per handelaar iets hard te coderen.

default_branch is de vestiging waar je API-client standaard op staat, als { "external_id", "name", "type" }. Het is null als de API-client er geen heeft. Een vestiging die is gedeactiveerd, wordt nog steeds genoemd. Een schrijfactie die op de standaard terugvalt, kan haar toch weigeren: een nieuwe transactie bij een gedeactiveerde vestiging geeft BRANCH_INACTIVE. Lees het veld om je eigen vestigingssleutel te kennen, in plaats van een kopie in je configuratie te bewaren.

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",
        "default_branch": {
            "external_id": "webshop",
            "name": "Webshop",
            "type": "online"
        }
    }
}

Actieve vestigingen ophalen

GET /api/v1/branches

Geeft de actieve vestigingen van je zaak terug, in de volgorde die jij aanhoudt. Een vestiging die is gedeactiveerd, staat er niet bij. De lijst is niet gepagineerd: data is de kale array. Vergelijk de vestigingssleutel die je hebt ingesteld met de external_id-waarden hier, of toon de namen aan de kassier.

Example request

curl https://puntjes.app/api/v1/branches \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

{
    "data": [
        {
            "external_id": "webshop",
            "name": "Webshop",
            "type": "online"
        }
    ]
}