API Ingest
The ingest endpoint is what the SDK calls to send an event. You can also call it directly from any HTTP client.
POST https://api.pionne.app/ingestAuthentication
Section titled “Authentication”X-Pionne-Token: pio_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPayload schema
Section titled “Payload schema”{ "exception_type": "TypeError", "message": "undefined is not an object (evaluating 'user.name')", "stack": "TypeError: undefined is not an object\n at ...", "level": "error", "release": "1.0.0", "environment": "production", "platform": "ios", "app_id": "com.acme.app", "tags": { "tier": "pro", "ab_test": "variant_b" }, "user": { "id": "user_a8f2c1" }, "contexts": { "device": { "manufacturer": "Apple", "model": "iPhone 15 Pro", "model_id": "iPhone16,1" }, "app": { "version": "1.0.0", "build": "42", "bundle_id": "com.acme.app" }, "os": { "name": "iOS", "version": "18.2" }, "expo": { "runtime_version": "1.0.0", "update_id": "abc123" } }, "mechanism": { "type": "global", "handled": false }, "breadcrumbs": [ { "category": "navigation", "message": "/checkout", "ts": 1714896000000 }, { "category": "http", "message": "GET https://api.example.com/cart", "ts": 1714896001000, "data": { "status": 200 } } ], "screenshot": "/9j/4AAQSkZJRgABA...", "extra": { "cart_id": 42 }}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
exception_type | string | Yes | E.g. TypeError, Error, custom |
message | string | Yes | Error message |
stack | string | null | No | Raw stack |
level | 'fatal' | 'error' | 'warning' | 'info' | No | Default error |
release | string | No, but recommended | For sourcemap matching |
environment | string | No | Default production |
platform | 'ios' | 'android' | Yes | Auto-set by the SDK |
app_id | string | Yes | Bundle ID, used for pinning |
tags | object | No | Scalar key/value pairs |
user.id | string | No | Anonymous only |
contexts | object | No | Auto-filled by the SDK |
mechanism | object | No | { type, handled } |
breadcrumbs | array | No | Max 50 |
screenshot | string | No | Base64 JPG |
extra | object | No | Free-form data |
Response
Section titled “Response”202 Accepted
Section titled “202 Accepted”{ "event_id": "evt_a1b2c3", "issue_id": "iss_xxx" }The event is queued for processing. It shows up in the dashboard within seconds.
Error codes
Section titled “Error codes”| Code | Cause | Solution |
|---|---|---|
400 | Malformed payload | Check the schema |
401 | Token invalid or revoked | Regenerate via /api/projects/{id}/regenerate |
403 | app_id doesn’t match the pinned bundle_id | See Bundle ID |
413 | Payload too heavy (often an uncompressed screenshot) | Lower screenshotQuality |
429 | Rate limit reached on this token. Response includes Retry-After. See Rate limits. | Lower sampleRate or bump maxEventsPerSecond SDK-side. |
SDK diagnostic — actionable warnings on permanent rejections
Section titled “SDK diagnostic — actionable warnings on permanent rejections”Since v0.8.6 (RN), v0.3.6 (Web/Node), v0.3.3 (PHP/Flutter), all official SDKs parse the response body on 401/403/422 and emit a single console.warn (or error_log / developer.log depending on the runtime) once per session, even in production builds. Network errors stay silent — only permanent config issues are surfaced.
The warning is shaped per failure mode:
| Status | Warning shape |
|---|---|
401 | [Pionne] Token rejected (401). Check that the project token (starts with "pio_live_…") matches. |
403 (bundle mismatch) | [Pionne] Bundle ID mismatch — your project rejects events from app_id="<sent>". Server expected "<masked>". |
422 | [Pionne] Event rejected (422 validation): <server message>. Sent app_id="<sent>". |
| Other 4xx | Generic [Pionne] Event rejected (status=<N>): <server message>. |
Why it matters: before this change, a misconfigured token or stale bundle pinning would silently drop every event without any signal in the host app’s console — devs would only notice when looking for a crash they triggered and finding nothing in the dashboard. Now the first rejected event surfaces a clear next-step in the runtime logs, even in TestFlight / staging / prod.
curl example
Section titled “curl example”curl -X POST https://api.pionne.app/ingest \ -H "Content-Type: application/json" \ -H "X-Pionne-Token: pio_live_xxx..." \ -d '{ "exception_type": "Error", "message": "manual test", "level": "info", "platform": "ios", "app_id": "com.acme.app" }'