Developers

The Tapp API

Create workspaces and feedback spots, push answers from your own systems, send WhatsApp requests and pull results into your reports. Plain JSON over HTTPS.

Getting started

Base address https://tapp.africa/api/v1

Send your key as Authorization: Bearer pk_live_…

Bodies and replies are JSON. Times are ISO 8601 in UTC. API access is part of the Business plan.

1. Make a key

In the dashboard: Settings → Connections → New key. Pick the permissions it needs and, if you like, limit it to some workspaces. The key is shown once; keep it on your server, never in a website or app.

2. Create a workspace

curl -X POST "https://tapp.africa/api/v1/workspaces" \
  -H "Authorization: Bearer $TAPP_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Store visit","template_key":"retail_store"}'
# returns the workspace and its first spot, with its link and QR code

3. Push feedback

curl -X POST "https://tapp.africa/api/v1/responses" \
  -H "Authorization: Bearer $TAPP_KEY" -H "Content-Type: application/json" \
  -d '{"spot":"<spot slug>","rating":4,"external_id":"sale-10045"}'

4. Read it back

curl "https://tapp.africa/api/v1/responses?since=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $TAPP_KEY"

Keys and permissions

Each key has only the permissions you give it. A key limited to some workspaces sees and changes only those, and can't create workspaces or locations. Revoke a key any time in Settings → Connections.

PermissionAllows
workspaces:readList workspaces, spots, locations and templates; QR codes
workspaces:writeCreate and update workspaces, spots and locations
responses:readRead feedback (internet addresses are never included)
responses:writePush feedback from your own systems
stats:readHeadline numbers, star ratings and suggestions
actions:readRead tasks
actions:writeCreate and update tasks
surveys:writeOne-time feedback links after a support ticket
whatsapp:sendSend feedback requests on WhatsApp

Errors and limits

Errors come back as { "error": "What went wrong" } with one of these codes. Up to 120 requests a minute per key. Lists are newest first.

CodeMeaning
400Something in the request is missing or wrong. The message says what.
401No key, or the key is wrong, expired or revoked.
402The plan does not include this (API access, WhatsApp, product ratings) or a plan limit was reached (spots, monthly answers).
403The key lacks the permission named in the message, or is limited to other workspaces.
404Not found, or outside the workspaces this key can see.
409Conflict, e.g. the contact replied STOP.
429Too many requests (120 a minute per key), or the contact was asked in the last 7 days.
Examples in

Workspaces and spots

A workspace is one feedback programme (e.g. "Store visit") with its question, answer type and reasons. Each workspace has one or more feedback spots: a link, QR code or website button where people answer. Usually one spot per branch, till or room.

List templates

GET /api/v1/templates workspaces:read

Ready-made workspaces with the question, answer type and reasons filled in. Pass a key as template_key when creating a workspace.

No parameters.

curl "https://tapp.africa/api/v1/templates" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": [
    {
      "key": "retail_store",
      "name": "How was the visit?",
      "industry": "retail",
      "kind": "general",
      "survey_type": "faces",
      "question": "How was your shopping experience today?",
      "reasons_negative": [
        "Long queue",
        "…"
      ],
      "reasons_positive": [
        "Friendly staff",
        "…"
      ]
    }
  ]
}

Create a location

POST /api/v1/locations workspaces:write

A branch, shop or site. Create locations first if you want one spot per location. Needs a key for all workspaces.

BodyTypeAbout
name*stringUp to 120 characters
kindstringstore, branch, clinic, hospital, airport, office, venue, online or other
addressstring
citystring
countrystringTwo letters, e.g. KE
lat, lngnumberMap position

* required

curl -X POST "https://tapp.africa/api/v1/locations" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Westlands branch",
    "kind": "branch",
    "city": "Nairobi",
    "country": "KE"
  }'

Reply · 201

{
  "data": {
    "id": "51bd…",
    "name": "Westlands branch",
    "kind": "branch",
    "address": null,
    "city": "Nairobi",
    "country": "KE",
    "lat": null,
    "lng": null
  }
}

List locations

GET /api/v1/locations workspaces:read

No parameters.

curl "https://tapp.africa/api/v1/locations" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": [
    {
      "id": "51bd…",
      "name": "Westlands branch",
      "kind": "branch",
      "address": null,
      "city": "Nairobi",
      "country": "KE",
      "lat": null,
      "lng": null
    }
  ]
}

Create a workspace

POST /api/v1/workspaces workspaces:write

Creates the workspace and its first spots: one per location in location_ids, or a single spot without a location. Start from a template, or set the question and answer type yourself. Needs a key for all workspaces.

BodyTypeAbout
name*stringUp to 120 characters
template_keystringFrom GET /templates. Fills in question, answer type and reasons
questionstringAsked at every spot. Default: the template's, or "How was your experience?"
survey_typestringfaces (4 faces, default), stars (1–5) or thumbs (up/down)
location_idsstring[]One spot is created per location
reasons_negativestring[]Up to 12 quick reasons shown after a bad answer
reasons_positivestring[]Up to 12 shown after a good answer
thank_youstringMessage after answering
allow_commentbooleanAsk for a comment (default true)
industrystringretail, health, services, employee, support, manufacturing, events or other
statusstringactive (default), draft, paused or ended

* required

curl -X POST "https://tapp.africa/api/v1/workspaces" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Store visit",
    "template_key": "retail_store",
    "location_ids": [
      "51bd…"
    ]
  }'

Reply · 201

{
  "data": {
    "id": "9a1e…",
    "name": "Store visit",
    "industry": "retail",
    "template_key": "retail_store",
    "kind": "general",
    "survey_type": "faces",
    "status": "active",
    "reasons_negative": [
      "Long queue",
      "Out of stock",
      "Staff attitude",
      "Price"
    ],
    "reasons_positive": [
      "Friendly staff",
      "Quick checkout",
      "Good prices"
    ],
    "created_at": "2026-10-20T08:12:44.120Z",
    "spots": [
      {
        "id": "3f0c…",
        "workspace_id": "9a1e…",
        "location_id": "51bd…",
        "name": "Store visit · Westlands branch",
        "slug": "store-visit-westlands-k3p",
        "question": "How was your shopping experience today?",
        "active": true,
        "created_at": "2026-10-20T08:12:44.120Z",
        "link": "https://tapp.africa/f/store-visit-westlands-k3p",
        "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
        "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
        "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
        "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
      }
    ]
  }
}

List workspaces

GET /api/v1/workspaces workspaces:read

Every workspace the key can see, each with its spots and their links.

No parameters.

curl "https://tapp.africa/api/v1/workspaces" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": [
    {
      "id": "9a1e…",
      "name": "Store visit",
      "industry": "retail",
      "template_key": "retail_store",
      "kind": "general",
      "survey_type": "faces",
      "status": "active",
      "reasons_negative": [
        "Long queue",
        "Out of stock",
        "Staff attitude",
        "Price"
      ],
      "reasons_positive": [
        "Friendly staff",
        "Quick checkout",
        "Good prices"
      ],
      "created_at": "2026-10-20T08:12:44.120Z",
      "spots": [
        {
          "id": "3f0c…",
          "workspace_id": "9a1e…",
          "location_id": "51bd…",
          "name": "Store visit · Westlands branch",
          "slug": "store-visit-westlands-k3p",
          "question": "How was your shopping experience today?",
          "active": true,
          "created_at": "2026-10-20T08:12:44.120Z",
          "link": "https://tapp.africa/f/store-visit-westlands-k3p",
          "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
          "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
          "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
          "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
        }
      ]
    }
  ]
}

Get a workspace

GET /api/v1/workspaces/{id} workspaces:read

No parameters.

curl "https://tapp.africa/api/v1/workspaces/9a1e…" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": {
    "id": "9a1e…",
    "name": "Store visit",
    "industry": "retail",
    "template_key": "retail_store",
    "kind": "general",
    "survey_type": "faces",
    "status": "active",
    "reasons_negative": [
      "Long queue",
      "Out of stock",
      "Staff attitude",
      "Price"
    ],
    "reasons_positive": [
      "Friendly staff",
      "Quick checkout",
      "Good prices"
    ],
    "created_at": "2026-10-20T08:12:44.120Z",
    "spots": [
      {
        "id": "3f0c…",
        "workspace_id": "9a1e…",
        "location_id": "51bd…",
        "name": "Store visit · Westlands branch",
        "slug": "store-visit-westlands-k3p",
        "question": "How was your shopping experience today?",
        "active": true,
        "created_at": "2026-10-20T08:12:44.120Z",
        "link": "https://tapp.africa/f/store-visit-westlands-k3p",
        "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
        "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
        "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
        "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
      }
    ]
  }
}

Update a workspace

PATCH /api/v1/workspaces/{id} workspaces:write

Send only what changes. A new question applies to every spot in the workspace. Pausing stops new answers.

BodyTypeAbout
namestring
statusstringactive, paused or ended
questionstring
reasons_negativestring[]
reasons_positivestring[]
curl -X PATCH "https://tapp.africa/api/v1/workspaces/9a1e…" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "How was your visit today?",
    "status": "active"
  }'

Reply · 200

{
  "data": {
    "id": "9a1e…",
    "name": "Store visit",
    "industry": "retail",
    "template_key": "retail_store",
    "kind": "general",
    "survey_type": "faces",
    "status": "active",
    "reasons_negative": [
      "Long queue",
      "Out of stock",
      "Staff attitude",
      "Price"
    ],
    "reasons_positive": [
      "Friendly staff",
      "Quick checkout",
      "Good prices"
    ],
    "created_at": "2026-10-20T08:12:44.120Z",
    "spots": [
      {
        "id": "3f0c…",
        "workspace_id": "9a1e…",
        "location_id": "51bd…",
        "name": "Store visit · Westlands branch",
        "slug": "store-visit-westlands-k3p",
        "question": "How was your shopping experience today?",
        "active": true,
        "created_at": "2026-10-20T08:12:44.120Z",
        "link": "https://tapp.africa/f/store-visit-westlands-k3p",
        "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
        "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
        "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
        "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
      }
    ]
  }
}

Add a spot

POST /api/v1/spots workspaces:write

Another place to collect answers in a workspace, e.g. a new till or branch. It asks the workspace's question unless you give one.

BodyTypeAbout
workspace_id*string
name*string
location_idstring
questionstring
thank_youstring
allow_commentboolean
brand_colorstringHex, e.g. #2F7D72

* required

curl -X POST "https://tapp.africa/api/v1/spots" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": "9a1e…",
    "name": "Till 2",
    "location_id": "51bd…"
  }'

Reply · 201

{
  "data": {
    "id": "3f0c…",
    "workspace_id": "9a1e…",
    "location_id": "51bd…",
    "name": "Store visit · Westlands branch",
    "slug": "store-visit-westlands-k3p",
    "question": "How was your shopping experience today?",
    "active": true,
    "created_at": "2026-10-20T08:12:44.120Z",
    "link": "https://tapp.africa/f/store-visit-westlands-k3p",
    "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
    "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
    "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
    "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
  }
}

Update a spot

PATCH /api/v1/spots/{id} workspaces:write

BodyTypeAbout
namestring
questionstring
thank_youstring
allow_commentboolean
activebooleanfalse stops new answers at this spot
location_idstring
curl -X PATCH "https://tapp.africa/api/v1/spots/9a1e…" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "active": false
  }'

Reply · 200

{
  "data": {
    "id": "3f0c…",
    "workspace_id": "9a1e…",
    "location_id": "51bd…",
    "name": "Store visit · Westlands branch",
    "slug": "store-visit-westlands-k3p",
    "question": "How was your shopping experience today?",
    "active": false,
    "created_at": "2026-10-20T08:12:44.120Z",
    "link": "https://tapp.africa/f/store-visit-westlands-k3p",
    "qr_link": "https://tapp.africa/f/store-visit-westlands-k3p?src=qr",
    "qr_image": "https://tapp.africa/api/v1/spots/3f0c…/qr.png",
    "embed_inline": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" async></script>",
    "embed_button": "<script src=\"https://tapp.africa/embed.js\" data-point=\"store-visit-westlands-k3p\" data-mode=\"button\" async></script>"
  }
}

QR code

GET /api/v1/spots/{id}/qr.png workspaces:read

The spot's QR code to print: qr.png (1024 px) or qr.svg. Scans are counted separately from link clicks.

No parameters.

curl "https://tapp.africa/api/v1/spots/3f0c…/qr.png" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -o qr.png

Reply · 200 · image/png

Feedback

Push answers from your own systems (till, booking app, call centre, kiosk) and read everything that comes in, from any channel.

Push feedback

POST /api/v1/responses responses:write

Send the answer the way the spot asks for it: rating for faces, stars for star spots, thumb for thumbs. Reasons must be ones set on the workspace; others are ignored. Send external_id (your sale or ticket number) so sending again never counts twice.

BodyTypeAbout
spot*stringSpot id or slug
ratingintegerFaces: 1 Bad, 2 Okay, 3 Good, 4 Great
starsintegerStar spots: 1–5
thumbstringThumbs spots: up or down
reasonsstring[]Up to 8, from the workspace's reasons
commentstringUp to 1,000 characters
external_idstringYour reference. A repeat returns the first answer with duplicate: true
created_atstringISO time it happened, if not now
agentobjectStar spots: { code | external_id | email, name }. With a name, a new agent is added
ticketstringStar spots: ticket number
batchstringProduct spots: batch or lot code

* required

curl -X POST "https://tapp.africa/api/v1/responses" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "spot": "store-visit-westlands-k3p",
    "rating": 2,
    "reasons": [
      "Long queue"
    ],
    "comment": "Only one till open",
    "external_id": "sale-10045"
  }'

Reply · 201

{
  "id": "7c21…",
  "duplicate": false
}

Read feedback

GET /api/v1/responses responses:read

Newest first, up to 500 a page. When next_cursor is not null, pass it as cursor for the next page. To sync, ask for everything since the last time you checked.

QueryTypeAbout
since, untilISO timeTime window
workspace, location, spotidNarrow down
rating1–4
stars1–5
channelstringlink, qr, nfc, embed, kiosk, whatsapp, api, ticket, desk, agent
reasonstringAnswers that picked this reason
qstringSearch comments
limit1–500Default 100
cursorstringFrom the previous page
curl "https://tapp.africa/api/v1/responses?since=2026-10-01T00:00:00Z&workspace=9a1e…&limit=100" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": [
    {
      "id": "7c21…",
      "created_at": "2026-10-20T09:30:02.511Z",
      "rating": 2,
      "stars": null,
      "thumb": null,
      "reasons": [
        "Long queue"
      ],
      "comment": "Only one till open",
      "answers": {
        "wait": "10–30 min"
      },
      "channel": "api",
      "workspace_id": "9a1e…",
      "location_id": "51bd…",
      "spot_id": "3f0c…",
      "country": "KE",
      "city": "Nairobi",
      "device_type": "mobile",
      "external_id": "sale-10045"
    }
  ],
  "next_cursor": null
}

Results

Headline numbers for dashboards and reports. range is 24h, 7d (default), 30d or 90d; narrow with workspace, location or spot.

Headline numbers

GET /api/v1/stats stats:read

No parameters.

curl "https://tapp.africa/api/v1/stats?workspace=9a1e…&range=30d" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": {
    "range": "30d",
    "total": 1240,
    "average": 3.21,
    "happy_percent": 82,
    "bad": 96,
    "okay": 127,
    "good": 402,
    "great": 615,
    "comments": 188
  }
}

Star ratings

GET /api/v1/ratings stats:read

For star spots (support, products).

No parameters.

curl "https://tapp.africa/api/v1/ratings?workspace=…&range=30d" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": {
    "range": "30d",
    "total": 312,
    "avg_stars": 4.32,
    "good_pct": 84,
    "low": 19,
    "stars": {
      "1": 8,
      "2": 11,
      "3": 30,
      "4": 88,
      "5": 175
    }
  }
}

Suggestions

GET /api/v1/insights stats:read

What needs attention and what to do about it. days: 1–90.

No parameters.

curl "https://tapp.africa/api/v1/insights?days=7" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": {
    "summary": "Happiness is down at Westlands, mostly about queues at lunchtime.",
    "happy_percent": 78,
    "total": 402,
    "findings": [
      {
        "key": "reason_spike",
        "severity": "warning",
        "title": "\"Long queue\" up 40% at Westlands",
        "detail": "…",
        "suggested_action": "Open a second till 12:00–14:00",
        "workspace": "Store visit",
        "scope": {
          "workspace": "9a1e…"
        }
      }
    ]
  }
}

WhatsApp

Ask a customer for feedback on WhatsApp after a sale or visit, from the business's own number. Needs WhatsApp connected in the dashboard. Requests go out at most 10 a minute, and each customer is asked at most once every 7 days.

Send a feedback request

POST /api/v1/whatsapp/send whatsapp:send

Adds the contact if new. They answer by tapping a face in the chat (or open a link). Their answer shows up in /responses with channel whatsapp.

BodyTypeAbout
spot*stringSpot id or slug
contact.phone*stringLocal (0712…) or international
contact.namestringUsed in the greeting
contact.external_idstringYour customer id
contact.tagsstring[]
consentstringHow they agreed to messages. Needed the first time
delay_minutesintegerWait before sending (up to 7 days)
stylestringbuttons (default) or link

* required

curl -X POST "https://tapp.africa/api/v1/whatsapp/send" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "spot": "store-visit-westlands-k3p",
    "contact": {
      "phone": "0712345678",
      "name": "Wanjiru"
    },
    "consent": "Ticked the box at checkout",
    "delay_minutes": 30
  }'

Reply · 202

{
  "data": {
    "send_id": "e40a…",
    "contact_id": "c9d2…",
    "scheduled_at": "2026-10-20T10:00:00.000Z",
    "style": "buttons"
  }
}

Support and tasks

Feedback link after a ticket

POST /api/v1/surveys surveys:write

A one-time link for a star (support) spot, tied to the ticket and agent. Send the url to the customer.

BodyTypeAbout
spot*stringA support spot id or slug
ticketstring
agentobject{ code | external_id | email, name }
expires_daysinteger1–90, default 14

* required

curl -X POST "https://tapp.africa/api/v1/surveys" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "spot": "rate-our-support-x2a",
    "ticket": "4512",
    "agent": {
      "external_id": "zd-101",
      "name": "Wanjiru Kamau"
    }
  }'

Reply · 201

{
  "data": {
    "id": "a77b…",
    "url": "https://tapp.africa/f/rate-our-support-x2a?i=…",
    "expires_at": "2026-11-03T10:00:00.000Z",
    "agent": {
      "id": "…",
      "name": "Wanjiru Kamau"
    },
    "ticket": "4512"
  }
}

List tasks

GET /api/v1/actions actions:read

No parameters.

curl "https://tapp.africa/api/v1/actions" \
  -H "Authorization: Bearer $TAPP_KEY"

Reply · 200

{
  "data": [
    {
      "id": "…",
      "title": "Open a second till at lunch",
      "detail": null,
      "severity": "warning",
      "status": "open",
      "due_at": "2026-10-25",
      "workspace_id": "9a1e…",
      "location_id": null,
      "created_at": "…",
      "resolved_at": null
    }
  ]
}

Create a task

POST /api/v1/actions actions:write

BodyTypeAbout
title*string
detailstring
severitystringcritical, warning or info
workspace_idstringRequired for keys limited to some workspaces
due_atstringYYYY-MM-DD

* required

curl -X POST "https://tapp.africa/api/v1/actions" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Call back customer about late delivery",
    "severity": "warning",
    "due_at": "2026-10-22"
  }'

Reply · 201

{
  "data": {
    "id": "…",
    "title": "Call back customer about late delivery",
    "status": "open",
    "created_at": "…"
  }
}

Update a task

PATCH /api/v1/actions/{id} actions:write

BodyTypeAbout
status*stringopen, in_progress, done or dismissed

* required

curl -X PATCH "https://tapp.africa/api/v1/actions/9a1e…" \
  -H "Authorization: Bearer $TAPP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "done"
  }'

Reply · 200

{
  "data": {
    "id": "…",
    "title": "…",
    "status": "done",
    "resolved_at": "…"
  }
}

Stuck, or need something the API doesn't do yet? Email [email protected].