API-referentie

Product-endpoints

Endpoints om je productcatalogus te synchroniseren en producten om te zetten in inwisselbare beloningen.

Puntjes herkent een product aan je eigen external id: de SKU/PLU uit je kassa of webshop, die bij jou uniek is. Prijzen staan in eurocenten. Push je catalogus met deze endpoints en bied daarna elk product aan als "gratis product"-beloning.

Idempotente synchronisatie

Synchroniseer je catalogus bij voorkeur met PUT /products/{external_id} (één product) of POST /products/batch (meerdere): allebei maken ze het product aan als het nog niet bestaat, en werken ze het bij als het er al staat. Je mag dezelfde catalogus dus zo vaak pushen als je wil, en je komt altijd op dezelfde rijen uit.


Producten weergeven

GET /api/v1/products

Geeft je producten terug, gepagineerd en op naam gesorteerd.

Queryparameters

ParameterTypeVerplichtBeschrijving
statusstringNeeFilter op status: active, inactive of archived
categorystringNeeFilter op exacte categorie
searchstringNeeZoekt in de productnaam, niet hoofdlettergevoelig
per_pageintegerNeePaginagrootte, 1–100 (standaard: 15)

Pagineren doe je met ?page=, de paginagrootte stel je in met ?per_page= (1–100, standaard 15). De paginator leest page zelf uit de querystring, dus dat veld staat niet in de parametertabel hierboven.

Response

{
    "data": {
        "data": [
            {
                "id": 1,
                "external_id": "SKU-1001",
                "name": "Cappuccino",
                "description": "A warm cup",
                "price_cents": 350,
                "image_url": "https://cdn.example.com/cappuccino.png",
                "category": "drinks",
                "stock": 20,
                "status": "active",
                "metadata": {
                    "barcode": "54000012"
                },
                "created_at": "2026-07-18T10:00:00+00:00",
                "updated_at": "2026-07-18T10:00:00+00:00"
            }
        ],
        "links": {
            "first": "https://puntjes.app/api/v1/products?page=1",
            "last": "https://puntjes.app/api/v1/products?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "last_page": 1,
            "per_page": 15,
            "total": 1
        }
    }
}

Een product aanmaken

POST /api/v1/products

Maak één product aan. De external_id moet uniek zijn binnen je eigen zaak; gebruik PUT voor idempotent aanmaken-of-bijwerken.

Request body

VeldTypeVerplichtBeschrijving
external_idstringJaJe eigen productidentifier (SKU/PLU), uniek per handelaar
namestringJaProductnaam
descriptionstringNeeProductbeschrijving
price_centsintegerNeePrijs in eurocenten (bv. 350 = EUR 3,50)
image_urlstringNeeAbsolute URL naar de productafbeelding
categorystringNeeVrij in te vullen categorielabel
stockintegerNeeBeschikbare voorraad (laat weg als je die niet bijhoudt)
statusstringNeeactive, inactive of archived (standaard: active)
metadataobjectNeeEigen sleutel/waarde-attributen die je ongewijzigd terugkrijgt

Example request

{
    "external_id": "SKU-1001",
    "name": "Cappuccino",
    "description": "A warm cup",
    "price_cents": 350,
    "image_url": "https://cdn.example.com/cappuccino.png",
    "category": "drinks",
    "stock": 20,
    "status": "active",
    "metadata": {
        "barcode": "54000012"
    }
}
curl -X POST https://puntjes.app/api/v1/products \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"SKU-1001","name":"Cappuccino","description":"A warm cup","price_cents":350,"image_url":"https://cdn.example.com/cappuccino.png","category":"drinks","stock":20,"status":"active","metadata":{"barcode":"54000012"}}'

Response (201 Created)

{
    "data": {
        "id": 1,
        "external_id": "SKU-1001",
        "name": "Cappuccino",
        "description": "A warm cup",
        "price_cents": 350,
        "image_url": "https://cdn.example.com/cappuccino.png",
        "category": "drinks",
        "stock": 20,
        "status": "active",
        "metadata": {
            "barcode": "54000012"
        },
        "created_at": "2026-07-18T10:00:00+00:00",
        "updated_at": "2026-07-18T10:00:00+00:00"
    }
}

Fouten

CodeStatusBeschrijving
PRODUCT_EXTERNAL_ID_DUPLICATE409Je hebt al een product met deze external_id

Een product aanmaken of bijwerken (upsert)

PUT /api/v1/products/{externalId}

Idempotent: bestaat het product nog niet, dan maakt deze call het aan, en anders vervangt hij het. De external_id komt uit de URL en zit dus niet in de body. Laat je een optioneel veld weg, dan wist Puntjes het, dus stuur telkens het volledige product mee. Dit is de bouwsteen waarmee je je catalogus synchroniseert.

Padparameters

ParameterTypeBeschrijving
externalIdstringJe eigen identifier voor het product (SKU/PLU)

Request body

Dezelfde velden als bij Een product aanmaken, zonder external_id. name is verplicht.

Example request

{
    "name": "Cappuccino",
    "description": "A warm cup",
    "price_cents": 350,
    "image_url": "https://cdn.example.com/cappuccino.png",
    "category": "drinks",
    "stock": 20,
    "status": "active",
    "metadata": {
        "barcode": "54000012"
    }
}
curl -X PUT https://puntjes.app/api/v1/products/SKU-1001 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Cappuccino","description":"A warm cup","price_cents":350,"image_url":"https://cdn.example.com/cappuccino.png","category":"drinks","stock":20,"status":"active","metadata":{"barcode":"54000012"}}'

Response

Je krijgt 201 Created als er een nieuw product is aangemaakt, en 200 OK als een bestaand product is bijgewerkt. De body is het productobject, met dezelfde vorm als bij Aanmaken.


Een product bijwerken

PATCH /api/v1/products/{externalId}

Werk een deel van een product bij. Alleen de velden die in de body staan, veranderen.

Padparameters

ParameterTypeBeschrijving
externalIdstringJe eigen identifier voor het product (SKU/PLU)

Request body

De velden bij Een product aanmaken die je wil wijzigen, zonder external_id (die ligt vast).

Example request

{
    "name": "Cappuccino",
    "description": "A warm cup",
    "price_cents": 350,
    "image_url": "https://cdn.example.com/cappuccino.png",
    "category": "drinks",
    "stock": 20,
    "status": "active",
    "metadata": {
        "barcode": "54000012"
    }
}
curl -X PATCH https://puntjes.app/api/v1/products/SKU-1001 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Cappuccino","description":"A warm cup","price_cents":350,"image_url":"https://cdn.example.com/cappuccino.png","category":"drinks","stock":20,"status":"active","metadata":{"barcode":"54000012"}}'

Fouten

CodeStatusBeschrijving
PRODUCT_NOT_FOUND404Je hebt geen product met deze external_id

Een product ophalen

GET /api/v1/products/{externalId}

Haal één product op met zijn external id.

Response

{
    "data": {
        "id": 1,
        "external_id": "SKU-1001",
        "name": "Cappuccino",
        "description": "A warm cup",
        "price_cents": 350,
        "image_url": "https://cdn.example.com/cappuccino.png",
        "category": "drinks",
        "stock": 20,
        "status": "active",
        "metadata": {
            "barcode": "54000012"
        },
        "created_at": "2026-07-18T10:00:00+00:00",
        "updated_at": "2026-07-18T10:00:00+00:00"
    }
}

Fouten

CodeStatusBeschrijving
PRODUCT_NOT_FOUND404Je hebt geen product met deze external_id

Een product verwijderen

DELETE /api/v1/products/{externalId}

Zet een product op verwijderd (soft delete). Het staat niet meer in de catalogus, maar beloningen die je er al van maakte, blijven werken. Push je dezelfde external_id opnieuw met PUT, dan staat het product er weer.

Response

204 No Content bij succes.

Fouten

CodeStatusBeschrijving
PRODUCT_NOT_FOUND404Je hebt geen product met deze external_id

Producten in bulk upserten

POST /api/v1/products/batch

Upsert tot 100 producten in één call: hiermee synchroniseer je een hele catalogus in één keer. Elk item wordt apart verwerkt. Gaat er één mis, dan krijgt dat item status error in zijn eigen resultaat en loopt de rest van de batch gewoon door. De call geeft altijd 200 OK terug, met een array met één resultaat per item en een samenvatting.

Example request

curl -X POST https://puntjes.app/api/v1/products/batch \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

{
    "data": {
        "results": [
            {
                "external_id": "SKU-1001",
                "index": 0,
                "product.external_id": "SKU-1001",
                "product.id": 1,
                "product.name": "Cappuccino",
                "product.price_cents": 350,
                "product.status": "active",
                "status": "created"
            }
        ],
        "summary": {
            "total": 2,
            "created": 1,
            "updated": 1,
            "failed": 0
        }
    }
}

De status van elk resultaat is created, updated of error. Bij error krijg je er een errors-object bij dat beschrijft welke velden misliepen.

Fouten

CodeStatusBeschrijving
BATCH_TOO_LARGE422De batch bevat meer dan 100 producten

Een beloning maken van een product

POST /api/v1/products/{externalId}/reward

Maak van een product een gratis product-beloning die klanten kunnen inwisselen. De beloning neemt de naam en de beschrijving van het product over, allebei te overschrijven, en blijft aan het product gekoppeld. Het inwisselen zelf loopt daarna via de gewone beloning- en inwisseling-endpoints.

Padparameters

ParameterTypeBeschrijving
externalIdstringJe eigen identifier voor het product (SKU/PLU)

Request body

VeldTypeVerplichtBeschrijving
point_costintegerJaPunten die het inwisselen kost (minimaal 1)
namestringNeeOverschrijf de beloningsnaam (standaard: productnaam)
descriptionstringNeeOverschrijf de beloningsbeschrijving (standaard: productbeschrijving)
total_stockintegerNeeBeloningsvoorraad (laat weg voor onbeperkt)
statusstringNeeactive of inactive (standaard: active)
available_fromstringNeeStartdatum (YYYY-MM-DD)
available_untilstringNeeEinddatum (YYYY-MM-DD)
code_valid_for_hoursintegerNeeGeldigheid van de inwisselcode in uren

Example request

{
    "point_cost": 150,
    "name": "Cappuccino",
    "description": "A warm cup",
    "total_stock": 50,
    "status": "active",
    "available_from": null,
    "available_until": null,
    "code_valid_for_hours": null
}
curl -X POST https://puntjes.app/api/v1/products/SKU-1001/reward \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"point_cost":150,"name":"Cappuccino","description":"A warm cup","total_stock":50,"status":"active","available_from":null,"available_until":null,"code_valid_for_hours":null}'

Response (201 Created)

{
    "data": {
        "id": 1,
        "product_id": 1,
        "name": "Cappuccino",
        "description": "A warm cup",
        "type": "free_product",
        "point_cost": 150,
        "image_url": "https://cdn.example.com/cappuccino.png",
        "total_stock": 50,
        "remaining_stock": 50,
        "status": {
            "value": "active",
            "label": "Active"
        },
        "available_from": null,
        "available_until": null,
        "discount_value": null,
        "discount_type": null,
        "product_reference": "SKU-1001",
        "code_valid_for_hours": null,
        "created_at": "2026-07-18T10:00:00+00:00",
        "updated_at": "2026-07-18T10:00:00+00:00"
    }
}

status.label is altijd Engels

De beloning komt terug met status als { "value": "active", "label": "Active" }. Deze API kent geen taal per request en vertaalt niets aan de serverkant, dus staat er in label het Engelse woord, welke taal je integratie ook spreekt. Vertaal value zelf, dat is active of inactive.

Fouten

CodeStatusBeschrijving
PRODUCT_NOT_FOUND404Je hebt geen product met deze external_id