Quickstart (no GitHub repo)
Onboard a customer when you can't give us the codebase — iOS/Android bundle ingest, Godot/Unity apps, anything where "just install the SDK" doesn't apply.
The default quickstart wires up the Layers SDK by cloning a GitHub repo, reading the code, and opening a PR. That path assumes (a) you have a repo, and (b) we can build it — which excludes a lot of real apps: Godot and Unity projects, React Native apps with custom native bridges, enterprise repos behind VPNs, indie apps shipped straight from a laptop, and any team not willing to hand us a token.
For those cases, skip the GitHub step. Point Layers at the App Store listing instead. The scraper extracts the same brand context we'd derive from the code — app name, subtitle, screenshots, keywords, review themes — and merges it into the project. You lose the auto-generated SDK PR; you keep everything else.
This page only covers the divergent step. Steps 1, 2, 3, and 6 of the main quickstart work the same way — start there for the whoami + create-project + polling boilerplate. Come back here for the ingest step.
Prerequisites
- The customer's app is live in the App Store, Play Store, or both.
- You have an App Machina layer installed on the project. App Store ingest routes to the
aso-auditworkflow, which writes its result into that layer — without it, the call 422s withmissing_app_machina_layer. - Steps 1 and 3 of the main quickstart complete — you have
$LAYERS_API_KEYand a$PROJECT_ID.
The divergent step: App Store ingest
Replace step 4 + 5 of the main quickstart with a single call:
curl -X POST "https://api.layers.com/v1/projects/$PROJECT_ID/ingest/appstore" \
-H "X-Api-Key: $LAYERS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"iosBundleId": "com.acmecoffee.ios",
"androidPackage": "com.acmecoffee.android",
"countryCode": "us"
}'import { randomUUID } from "node:crypto";
const res = await fetch(
`https://api.layers.com/v1/projects/${projectId}/ingest/appstore`,
{
method: "POST",
headers: {
"X-Api-Key": process.env.LAYERS_API_KEY!,
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({
iosBundleId: "com.acmecoffee.ios",
androidPackage: "com.acmecoffee.android",
countryCode: "us",
}),
},
);
const { jobId, locationUrl } = await res.json();import os, uuid, requests
res = requests.post(
f"https://api.layers.com/v1/projects/{project_id}/ingest/appstore",
headers={
"X-Api-Key": os.environ["LAYERS_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"iosBundleId": "com.acmecoffee.ios",
"androidPackage": "com.acmecoffee.android",
"countryCode": "us",
},
)
envelope = res.json()
job_id = envelope["jobId"]Response:
{
"jobId": "01JS5V8KX5JFK1YQ4G2YZQ4XEP",
"kind": "appstore_ingest",
"status": "running",
"stage": "scraping",
"projectId": "9cb958b5-11b5-4e30-8675-5d075d52da7c",
"locationUrl": "/v1/jobs/01JS5V8KX5JFK1YQ4G2YZQ4XEP",
"startedAt": "2026-04-18T19:25:42.187Z"
}Poll locationUrl exactly like the main quickstart's step 6 — same envelope, same terminal states. Most scrapes land in under 5 seconds; expect longer for non-US storefronts or apps with a large review corpus.
Body fields
At least one of iosBundleId, androidPackage, or appStoreUrl is required. You can send more than one and we'll scrape each storefront.
| Field | Type | Notes |
|---|---|---|
iosBundleId | string | Reverse-DNS iOS bundle (e.g. com.acmecoffee.ios). |
androidPackage | string | Android package name (e.g. com.acmecoffee.android). |
appStoreUrl | string | Direct store URL. Use this for Godot/Unity exports where the bundle/package is awkward to look up. |
countryCode | string | ISO 3166-1 alpha-2. Defaults to us. |
What comes back
On completion, result.brandContext is merged into the project and written to the App Machina layer. You can read the final shape via GET /v1/projects/:projectId:
{
"id": "9cb958b5-11b5-4e30-8675-5d075d52da7c",
"name": "Acme Coffee",
"brand": {
"appName": "Acme Coffee",
"tagline": "Skip the line. Sip the best.",
"audience": "coffee-first iOS users, 22–40, urban US",
"brandVoice": "warm, casual, a little snobby about beans",
"keywords": ["mobile ordering", "loyalty", "specialty coffee"]
}
}That brand block is the same shape the GitHub ingest produces. Downstream — content generation, influencer narratives, ad creative — consume it the same way. The only thing missing versus the GitHub path is the auto-generated SDK install PR; for Godot/Unity you wire the SDK in manually (SDK docs).
What to do when the scrape fails
The scraper has a 5-failure / 7-day circuit breaker per app. Repeated failures (wrong bundle ID, delisted app, geo-blocked storefront) trip the breaker and subsequent calls return SCRAPE_FAILED until the cooldown ends.
Typical failure modes:
VALIDATION422 withreason: "missing_app_machina_layer"— install the App Machina layer on the project first; re-run.SCRAPE_FAILEDon the job — the bundle/package doesn't resolve on that storefront. Double-check thecountryCodematches where the app is actually listed.NOT_FOUNDon the project —$PROJECT_IDdoesn't belong to your org.
Godot / Unity / non-Node-repo specifics
If the app has no Node-buildable code in the repo, the GitHub ingest path has nothing to analyze and will fail before the PR step. Use the App Store path instead — the scraper doesn't care what engine the app is built with, it only reads the store listing.
For the SDK itself, the onboarding guide has manual install recipes. The short version: register the customer's iosBundleId / androidPackage via POST /v1/projects/:id/sdk-apps, pull installSpec off the response, and drop the platform-native bootstrap code into the binary by hand.
See also
- Quickstart (GitHub path) — shared steps 1, 2, 3, 6
POST /v1/projects/:projectId/ingest/appstore— full endpoint reference- Onboard a customer — longer walk-through including manual SDK install
- Jobs — the polling envelope