POST /v1/projects/:projectId/sdk-apps
Provision an SDK app for a project. Returns the ingest endpoint and API key once.
/v1/projects/:projectId/sdk-apps- Auth
- Bearer
- Scope
- projects:write
Creates an SDK app and creates its ingest API key. An SDK app is the binding between a platform build (iOS bundle, Android package, web domain) and the Layers event pipeline - once installed, the client SDK sends events to in.layers.com/l/events authenticated with the key returned here.
The API key in the response is shown once. Store it immediately - Layers does not display it again. Rotation is available via PATCH /v1/projects/:projectId/sdk-apps/:appId.
projectIdstringrequiredProject ID.
Idempotency-Keystring (UUID)requiredSee [Idempotency](/docs/api/operational/idempotency). Recommended on every POST.
namestringrequiredHuman-readable app name, 1–128 chars.platformstringrequiredTarget platform.One of:ios,android,web,react-native,flutter,expo,nextjs,vite,react-routerbundleIdstringoptionaliOS bundle identifier, required when platform is ios.androidPackagestringoptionalAndroid package name, required when platform is android.webDomainstringoptionalApex domain for web / Next.js / Vite / React Router installs, required when platform is one of those.
Example request
curl https://api.layers.com/v1/projects/prj_9cb958b5-11b5-4e30-8675-5d075d52da7c/sdk-apps \
-H "Authorization: Bearer lp_..." \
-H "Idempotency-Key: 2f0e1c88-4b1d-4ac1-bc0a-5e9f6d8a7b10" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Coffee iOS",
"platform": "ios",
"bundleId": "com.acmecoffee.ios"
}'const app = await layers.sdkApps.create(
"9cb958b5-11b5-4e30-8675-5d075d52da7c",
{
name: "Acme Coffee iOS",
platform: "ios",
bundleId: "com.acmecoffee.ios",
},
{ idempotencyKey: crypto.randomUUID() }
);
// Store app.apiKey now - it won't be shown again.app = layers.sdk_apps.create(
project_id="prj_9cb958b5-11b5-4e30-8675-5d075d52da7c",
name="Acme Coffee iOS",
platform="ios",
bundle_id="com.acmecoffee.ios",
idempotency_key=str(uuid.uuid4()),
)
# Store app["apiKey"] now - it won't be shown again.Response
{
"appId": "app_8ffb9410eb0eb848264f8a65",
"name": "Acme Coffee iOS",
"platform": "ios",
"bundleId": "com.acmecoffee.ios",
"androidPackage": null,
"webDomain": null,
"ingestEndpoint": "https://in.layers.com/l/events",
"capi": {},
"createdAt": "2026-04-18T19:25:22Z",
"lastRotatedAt": "2026-04-18T19:25:22Z",
"lastEventAt": null,
"apiKey": "sk_app_8eRq...k2P"
}capi is an empty object until you enable per-platform forwarding via PATCH. lastRotatedAt is set to createdAt on first creation.
apiKey is returned only on creation and rotation. If you lose it, rotate via PATCH to create a new one - the previous key is invalidated immediately.
Errors
| Status | Code | When |
|---|---|---|
| 422 | VALIDATION | :projectId is not a UUID, platform unknown, or missing platform-specific identifier (bundleId, androidPackage, webDomain). |
| 401 | UNAUTHENTICATED | Missing or invalid key. |
| 403 | FORBIDDEN_SCOPE | Key lacks projects:write. |
| 404 | NOT_FOUND | Project does not exist in the key's organization. |
| 409 | IDEMPOTENCY_CONFLICT | Same Idempotency-Key replayed with a different body. |
| 409 | CONFLICT | SDK app with the supplied (platform, bundleId/androidPackage/webDomain) already exists. |
| 422 | VALIDATION_FAILED | Body includes appId (resource ids are server-minted — see Idempotency). |
| 429 | RATE_LIMITED | Write budget exhausted. |
See also
GET /v1/projects/:projectId/sdk-apps/:appId/install-spec- deterministic install snippetPATCH /v1/projects/:projectId/sdk-apps/:appId- rotate key, update CAPI- SDK events - read what the SDK sends