SDK key
Embedded in your iOS app and used by the SDK. Sent as X-API-Key. It is the only key that belongs in a shipped binary. Do not call SDK endpoints from your own backend with it.
wandkit apikeys create
The wandkit developer surface is three things: an iOS SDK inside your app, a command line tool for your repo and your CI, and one HTTP API that both the CLI and the dashboard speak. This page describes what is actually served today.
wandkit issues three kinds of key and they are never interchangeable. A server key sent to an SDK endpoint is rejected, and an SDK key sent to a server endpoint is rejected. There is no live and test split. Keys are named, revocable, and the plaintext is shown once at creation and never again.
Embedded in your iOS app and used by the SDK. Sent as X-API-Key. It is the only key that belongs in a shipped binary. Do not call SDK endpoints from your own backend with it.
wandkit apikeys create
For your backend. Sent as X-API-Key. Today it authenticates exactly two endpoints, both listed further down this page. That is the whole server-to-server surface, and we would rather say so than imply more.
For you, the CLI, scripts and agents. Sent as Authorization: Bearer. Scoped read or write, expires in 30, 90 or 365 days, revocable at any time.
A token reaches the same API the dashboard uses, minus the operations that should stay tied to a signed-in session.
The spec classifies every operation that accepts a Bearer credential, and a test fails the build if a new endpoint arrives unclassified. Of the 147 such operations, 126 accept a personal access token and 21 are session-only. The session-only set is the account-critical work: creating further tokens, deleting your account, changing organization membership, and storing integration secrets for APNs, RevenueCat, App Store Connect and Google Play. A token cannot mint another token.
403 with the code token_read_only on anything else.# read the last 50 checkout_completed events for a project curl -H "Authorization: Bearer $WANDKIT_TOKEN" \ "https://api.wandkit.app/api/v1/projects/$PROJECT/events?event_name=checkout_completed&limit=50"
wandkit from the command line, for humans, scripts and agents. Authenticates with a personal access token from WANDKIT_TOKEN or a stored login.
Forms live in your repo as forms/, one file per form. The key is the only identity, so files carry no project id or version and the same file can be pushed to staging and then to production. pull, push, diff, validate, list.
list for one page, export for all of them, auto-paginated, filtered with --since and --until. NDJSON by default, one object per line, so it pipes straight into whatever you already use. --format json if you want an array.
list and tail. This is the tool for answering "is my SDK actually sending anything", and for discovering the event names you will trigger surveys on. It is a raw log today, not analytics.
Issues: list, create, update, close. Campaigns: list, get, create, update, delete, metrics. Plus projects, apikeys for SDK keys, auth and config.
# authenticate once, then bind this directory to a project wandkit auth login --with-token < token.txt wandkit projects use# writes .wandkit.yml # forms as code: edit locally, review the diff, promote wandkit forms pull # every form to forms/ .json wandkit forms diff forms/nps.json --projectwandkit forms push forms/nps.json --project # responses out as NDJSON wandkit responses export --form --since 30d > responses.ndjson # watch events land while you wire up the SDK wandkit events tail
No workflow should be blocked on a missing wrapper command, so the CLI carries a raw authenticated request. Paths may omit the /api/v1 prefix. --paginate follows the standard envelope and merges every page; --input takes a file path or - for stdin.
wandkit api GET /me/tokens wandkit api GET /projects//issues --paginate wandkit api POST /projects/ /issues --input issue.json
.wandkit.yml is committed alongside your code and pins the project and forms directory, so CI jobs and agents need no flags. WANDKIT_TOKEN always takes precedence over a stored login, so a CI runner never needs a credentials file on disk. The CLI is not yet published for download. Write to info@wandkit.app and we will get you a build.
Two endpoints take a wks_ server key. This is the complete list, not a selection.
POST /api/v1/push/send queues one notification for every device that user has registered. Delivery is asynchronous. Requires an APNs integration on the project. A repeated idempotency_key returns the original notification rather than queueing a second one, so retries are safe.
One caution we would rather state than bury: a device binds itself to a user id using the SDK key embedded in your app, so a determined client can bind to somebody else's id. Treat notification content as readable by anyone, and never put secrets or account data in it.
curl -X POST https://api.wandkit.app/api/v1/push/send \ -H "X-API-Key: wks_..." \ -H "Content-Type: application/json" \ -d '{"external_user_id":"user_123", "title":"Your report is live", "body":"Thanks for the bug report.", "idempotency_key":"report-live-8811"}'
POST /api/v1/referrals/conversions. The app claims a referral first, then hands its install id to your backend, which reports the signup here. This call is deliberately yours to make: only your backend knows what a real signup is.
It always returns 200. A signup on an install that never claimed a referral is an ordinary organic signup, not an error, and comes back as converted: false with reason: no_claim. Reporting the same external_user_id twice is a no-op that returns the original outcome. When a conversion reaches the campaign's threshold, the inviter's reward is enqueued in the same transaction.
curl -X POST https://api.wandkit.app/api/v1/referrals/conversions \ -H "X-API-Key: wks_..." \ -H "Content-Type: application/json" \ -d '{"install_id":"...","external_user_id":"user_123"}'
The spec is the contract. These conventions hold across the API.
The OpenAPI 3.0.3 document is served by the API itself, and every request under /api/v1 is validated against it at runtime, so the request contract cannot drift from the running server. Swagger UI is at /swagger.
Branch on code, a stable snake_case string. Never branch on message, which is localized and free to change.
One envelope across list endpoints. limit defaults to 20 and caps at 100. wandkit api --paginate walks it for you.
Worth knowing before you plan around it. This is the surface as served today, not a statement about what is planned.
wk_live_ or wk_test_. Use separate projects for staging and production, which is also what makes forms-as-code promotion work.code field is language-independent.Something you need that is not here? Write to info@wandkit.app and tell us what you were trying to build. During beta, that is how the API gets decided.