Security & compliance
PII handling, encryption, subprocessors, partner authentication, key revocation, kill-switch.
How we handle your data and your customers' data, what third parties touch it, and how to revoke access in a hurry.
Security contact: security@layers.com. PGP key available on request.
Commercial terms (signed DPA, custom retention, security questionnaires, audit reports) are negotiated at contract time. Contact legal@layers.com.
What data flows where
Three classes of data cross the network:
- API integration traffic. The bytes you send to
api.layers.com/v1/*. Metadata (org, project, container ids) is logged with the request; the body is not persisted beyond in-memory request scope. - Customer SDK events. Events your app emits via the Layers SDK to
in.layers.com. These carry the app-supplieduser_id, optionalemail, optionalphone, device identifiers, and the event properties you put on the payload. See PII handling at the SDK ingest below. - Generated + inbound social content. Video/image assets generated for your projects, plus public-post metadata pulled from connected social accounts.
PII handling at the SDK ingest
The SDK ingest endpoint at in.layers.com is the only customer-PII entry point. Before events are persisted, email and phone are normalized and SHA-256 hashed in process, so plaintext email and phone are never written to storage. Device identifiers and IP addresses are stored in plaintext, as the table below lists.
| Field | Treatment |
|---|---|
email | Lower-cased, trimmed, SHA-256 hashed before persist + before CAPI forwarding. |
phone | Digits-only normalization, then SHA-256. |
user_id | Stored as-is. This is the id you assign - we recommend it not carry PII. |
app_user_id, revenuecat_original_app_user_id | Stored as-is. |
| IP address | Stored in plaintext. The ingest edge records the device's IP address on every SDK event, and on tracking-link clicks and SKAdNetwork postbacks. It is also resolved to country, region and city at ingest. |
| User agent | Stored in plaintext on SDK events and tracking-link clicks. |
| IDFA (iOS) | Stored in plaintext on SDK events. The SDK includes it only after the user authorizes App Tracking Transparency (ATT). |
| GAID (Android) | Stored in plaintext on SDK events whenever the Android SDK reports one, in the same advertising-id field as the IDFA. |
| IDFV and install id | Stored in plaintext on SDK events. |
Ad click ids (fbclid, gclid, ttclid and similar) | Stored in plaintext on tracking-link clicks and on SDK events that carry them. |
If your SDK integration sends emails with inconsistent casing or whitespace, the hash still matches because we normalize before hashing.
Why device identifiers and IP addresses are kept
- Attribution. Click ids give a deterministic match between an ad click and an install. When no click id is present, Layers matches an install to a click by IP address and device signals inside a window of up to 60 minutes.
- Fraud and bot filtering. User agents and device signals are used to recognise crawlers and link prefetchers so their clicks are not credited with installs.
- Conversion forwarding. When you enable conversion forwarding (CAPI) for an ad platform, the IP address, user agent and advertising id are sent to that platform with the conversion event. Nothing is forwarded while forwarding is off.
These records are retained while your account is active. To delete an end user's records, see Data-subject requests.
Compliance scope
- HIPAA - Not in scope. Layers does not process PHI. Partners must not transmit PHI via the API or the SDK.
- PCI DSS - Not in scope. Layers does not handle cardholder data; billing is delegated to Stripe.
- GDPR / CCPA - DPA available on request. See Data protection.
For security questionnaires, audit artifacts, or a signed DPA, email legal@layers.com.
Encryption
At rest. Data is encrypted at rest with AES-256. Secrets such as OAuth tokens, webhook signing secrets, and CAPI access tokens receive additional encryption and are decrypted only when needed. Object storage for generated media uses provider-managed encryption keys.
In transit. TLS 1.2 or higher required everywhere. api.layers.com, in.layers.com, and layers.com terminate TLS at a managed edge with managed certificates; HTTP traffic is redirected to HTTPS. Internal service-to-service traffic runs over private networking.
Subprocessors
Current material subprocessors include:
Material sub-processor changes are announced to affected partners. The commercial side of this - advance-notice windows, objection rights, contractual remedies - is governed by your DPA.
| Subprocessor | Purpose | Data touched |
|---|---|---|
| Google Cloud Platform | Application hosting and object storage | All application traffic, generated media, event stream |
| Supabase | Application database and auth | API integration state, customer event records (hashed PII), OAuth tokens |
| Google Cloud Memorystore | Rate-limit counters | API key ids, per-minute and per-day request counts |
| Sentry | Error tracking | Stack traces, request metadata (no bodies) |
| PostHog | Product analytics (internal) | Layers-employee product usage; no partner-visible data |
| Google Vertex AI / Gemini Developer API | LLM inference (content generation, scoring, repository analysis) | Campaign and brand/profile text; repository file paths, bounded config/source excerpts, bundle/URL matches, selected product images |
| OpenAI | LLM inference (select pipelines) | Campaign prompts and brand/profile text; per-request only |
| Anthropic | LLM inference (select pipelines) | Campaign prompts and brand/profile text; per-request only |
| E2B | Isolated repository analysis | Temporary repository checkout, selected source/config/assets, and a transient repository credential for private repositories; Layers requests sandbox deletion after execution |
| Firecrawl | Public website/app-store scraping and brand evidence | Submitted or discovered public URLs and rendered public page content, metadata, and branding; current requests permit vendor caching |
| Mastra Platform | Hosted Elle runtime and observability | Agent/workflow telemetry, logs, traces, and operational metadata; captured spans may include workflow inputs/outputs |
| Replicate | Model hosting (image/video model inference) | Generation inputs; per-request only |
| Stripe | Credit purchases + partner billing | Partner billing contact, invoice metadata |
| Resend | Transactional email | Partner recipient email; email body |
| Loops | Lifecycle email (Layers account-owner only) | Layers-employee email only |
| Cloudflare | Edge DNS + WAF on in.layers.com | Request metadata only; no bodies stored |
Partner authentication
API integration keys are opaque production credentials. Only a non-recoverable representation is stored.
Keys are never recoverable after issuance - we store only the bcrypt hash. If you lose a key, rotate it; we cannot send you the original.
Headers partners send
Authorization: Bearer lp_...See Authentication for the wire-level specifics.
Key revocation
Three ways a key stops working, in operational precedence order:
- Kill switch (
key.kill_switch = true). Immediate. Responses carry503 KILL_SWITCH. Used for runaway clients or emergency shutoffs. Reversible without re-issuing the key. See Rate limits → Kill switch. - Soft revoke (
is_active = falseorrevoked_atset). Permanent. Responses carry401 REVOKED. Used when a key is compromised, superseded, or the partnership ends. Not reversible - issue a fresh key. - Organization-level kill (
kill_switch.scope = "organization"). All keys for the org fail with503 KILL_SWITCH. Contractual remedy only.
Self-serve revocation
POST /v1/api-keys/:keyId/kill- emergency kill-switch (scoped to your org).POST /v1/api-keys/:keyId/rotate- rotate the secret, old one becomes invalid on the next request.DELETE /v1/api-keys/:keyId- retire a key without tripping the kill-switch.
When to revoke
- A key appeared in a git commit, a log file, or any place you can't scrub completely.
- An employee with access to a key left.
- Any suspicion the secret is out of your control.
Rotating is free. Revoking is free. We'd rather see five false alarms than miss one real leak.
Kill-switch semantics
The kill switch is a per-key (or per-org, or global) boolean that short-circuits request handling after authentication but before business logic. It returns 503 KILL_SWITCH with details.scope ∈ {"key", "organization", "global"}.
We flip it ourselves in two cases:
- Runaway client - you're paging an upstream platform at a rate that risks getting Layers or your app rate-limited by Meta/TikTok/Apple. We'd rather stop you than let a cascading ban happen. You'll hear from us on your partner channel.
- Upstream incident - a platform is down in a way where retrying makes things worse. Global kill buys the ecosystem time to recover.
A kill is never silent. We post to the support and message your partner contact.
Responsible disclosure
If you find a vulnerability, email security@layers.com with repro steps and the requestId of any affected request. We aim to reply promptly and will not pursue legal action against good-faith researchers operating within the standard safe-harbor terms.
Layers does not currently run a paid bug-bounty program.
Please do not open public GitHub issues for vulnerabilities.
See also
Data protection
Controller/processor roles, data categories, retention, US residency, DPA request channel.
Overview
The Layers SDK instruments your app for attribution and event tracking — install it once per platform, then track the events that matter. Available for Expo, React Native, iOS, Android, Flutter, Unity, Web, and Node.