Build on KioskCluster

One GraphQL API behind the dashboard and every storefront: the calls our own apps make, with refusals you can act on.

API at https://api.kioskcluster.comGuides for merchants
On this page

01Two endpoints

Everything is GraphQL over HTTPS: a POST of JSON with a query and its variables. The dashboard and every storefront use these same endpoints.

EndpointForSends
/graphqlRunning a business: signing staff in, the merchant, its businesses and teamauthorization: Bearer kcs_…, except on the sign-in calls
/shop/graphqlA storefront, and its shoppers' accountsauthorization: Bearer kcf_…, and kioskcluster-customer: Bearer kcc_… once a shopper signs in

Both live at https://api.kioskcluster.com.

02Your first call

Sign-in calls carry no token. Ask for a code for an email address or phone number that has a KioskCluster account:

shell
curl https://api.kioskcluster.com/graphql \
  -H 'content-type: application/json' \
  -d '{
    "query": "mutation RequestLoginCode($identifier: String!) { auth { requestLoginCode(identifier: $identifier) { challengeId identifierType resendAvailableAt } } }",
    "variables": { "identifier": "you@yourshop.com" }
  }'

The code goes to that email or phone. Send it back with the challengeId to get a session token:

graphql
mutation VerifyLoginCode($challengeId: ID!, $code: String!) {
  auth {
    verifyLoginCode(challengeId: $challengeId, code: $code) {
      token      # kcs_… a staff session, for the Authorization header
      expiresAt
      userId
      isNewUser
    }
  }
}

Then ask who you are, and which merchants you belong to:

shell
curl https://api.kioskcluster.com/graphql \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer kcs_…' \
  -d '{ "query": "query Start { auth { me { id name email phone } } organizations { myOrganizations { id name slug status } } }" }'

03Tokens

Every token is a four-letter prefix and 43 characters of base64url, sent as Bearer. The prefix says what it is for:

PrefixWhat it isLasts
kcs_A staff member's session, from signing inUp to 30 days, or until signed out
kck_A business's API key, for your own servers. Owners and admins manage them, and signing people out never stops themUntil revoked
kcf_A storefront's token. It is public by design: it only says which store is askingNo sign-in behind it
kcc_A shopper's session on one storeUp to 30 days, or until signed out
Keep kcs_ and kck_ tokens on a server. Never put them in a web page, a mobile app or a repository. A kcf_ token is the only one meant to be seen.

04Signing in

Staff sign in on /graphql under auth, and shoppers on /shop/graphql under account, the same three ways:

  • A code: requestLoginCode(identifier) sends 4 digits to the email or phone, then verifyLoginCode(challengeId, code) answers a token. A code works for 10 minutes and 5 tries. A new one can be sent after 60 seconds, and at most 10 an hour.
  • A password, once one is set: loginWithPassword(identifier, password). Passwords are 10 to 200 characters, and lock for 15 minutes after 5 wrong tries.
  • Google: signInWithGoogle(idToken, nonce), with an ID token from Google Identity Services that is at most 10 minutes old.

A signed-in person can list their sessions (mySessions: device, IP address, started, last used, expires), end one (revokeSession), or every other (signOutOtherSessions).

05Storefronts

A storefront sends its public kcf_ token on every call. When a shopper signs in, the reply's token goes in a second header, so the store and the shopper are never confused:

http
POST https://api.kioskcluster.com/shop/graphql
authorization: Bearer kcf_…               # the store's public token
kioskcluster-customer: Bearer kcc_…       # only once a shopper has signed in

query ShopMe { account { me { customerId name emails phones hasPassword } } }

If the shopper's session has expired, the server refuses the call. Drop the second header and carry on as a guest.

06Refusals

Read a refusal from errors[0].extensions.code, never from the HTTP status. The message is written to be shown to a person.

json
{
  "data": null,
  "errors": [
    {
      "message": "orders:manage is not in your role",
      "extensions": { "code": "MISSING_PERMISSION" }
    }
  ]
}
CodeWhat it means
VALIDATION_ERRORAn argument is wrong. The message says which, in words you can show.
UNAUTHENTICATEDNo token, or one that has expired or been signed out. Sign in again.
MISSING_BUSINESS_SCOPEThe call names a merchant you are not a member of.
MISSING_PERMISSIONYour role doesn't include what the call needs. The message names the permission.
MERCHANT_SUSPENDEDThe merchant is suspended, for example over an unpaid plan.
PLAN_LIMITThe plan's limit is reached, such as its number of products or staff accounts.
NOT_IN_PLANThe feature isn't part of the merchant's plan.
NOT_FOUNDNothing with that id, or nothing you can see.
RATE_LIMITEDToo many attempts. Wait, then try again; the message says what to wait for.
NOT_CONFIGUREDA way to sign in, such as codes by email or Google, isn't set up. Use another.
INTERNALOur fault. Try again shortly.

A query that isn't valid GraphQL answers GraphQL's own GRAPHQL_PARSE_FAILED, GRAPHQL_VALIDATION_FAILED or BAD_USER_INPUT.

07Roles and permissions

A call can do only what the signed-in person's role allows. A role is given for the whole merchant, for one business, or for one location. Reports, settings, the team, billing, API keys and opening a business never come with a role for a single location.

PermissionOwnerAdminManagerStaffAccountant
orders:view
See orders
YesYesYesYesYes
orders:manage
Confirm, update and cancel orders
YesYesYesYesNo
refunds:decide
Approve or refuse refunds
YesYesYesNoYes
products:view
See products
YesYesYesYesNo
products:manage
Add and change products
YesYesYesNoNo
stock:view
See stock
YesYesYesYesNo
stock:adjust
Change stock counts
YesYesYesNoNo
customers:view
See customers
YesYesYesYesNo
customers:manage
Change customers
YesYesYesNoNo
reports:view
See reports
YesYesYesNoYes
settings:manage
Change the business's settings
YesYesNoNoNo
members:view
See the team
YesYesYesNoNo
members:manage
Invite and change the team
YesYesNoNoNo
billing:view
See the plan and invoices
YesYesNoNoYes
billing:pay
Pay for the plan
YesYesNoNoNo
apikeys:manage
Create and revoke API keys
YesYesNoNoNo
businesses:create
Open another business
YesNoNoNoNo

08Limits

  • A storefront's calls are limited to 1,200 a minute, and repeated calls with a bad storefront token are slowed down.
  • Failed sign-ins are slowed down too. Over the limit, a call answers RATE_LIMITED; wait, then try again.
  • Plans set how many storefronts, products and staff accounts a merchant has. Going over answers PLAN_LIMIT. See Compare plans.

09Payments and Hubtel

Customers pay through the merchant's own Hubtel account. Hubtel tells KioskCluster about each payment at the store's own webhook, shown in the dashboard under Settings, Payments:

url
https://api.kioskcluster.com/webhooks/hubtel/<your-store-link>

An order counts once Hubtel confirms its payment. An unpaid order is released after 30 minutes by default; the merchant can change that.

10Getting an API key

API keys belong to a business, and only its owner or an admin can have one made. Write to support@kioskcluster.com from that account with your store's link and what you are building.

Start free14 days, everything on