Skip to content

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/ingest
X-Pionne-Token: pio_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{
"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
}
}
FieldTypeRequiredDescription
exception_typestringYesE.g. TypeError, Error, custom
messagestringYesError message
stackstring | nullNoRaw stack
level'fatal' | 'error' | 'warning' | 'info'NoDefault error
releasestringNo, but recommendedFor sourcemap matching
environmentstringNoDefault production
platform'ios' | 'android'YesAuto-set by the SDK
app_idstringYesBundle ID, used for pinning
tagsobjectNoScalar key/value pairs
user.idstringNoAnonymous only
contextsobjectNoAuto-filled by the SDK
mechanismobjectNo{ type, handled }
breadcrumbsarrayNoMax 50
screenshotstringNoBase64 JPG
extraobjectNoFree-form data
{ "event_id": "evt_a1b2c3", "issue_id": "iss_xxx" }

The event is queued for processing. It shows up in the dashboard within seconds.

CodeCauseSolution
400Malformed payloadCheck the schema
401Token invalid or revokedRegenerate via /api/projects/{id}/regenerate
403app_id doesn’t match the pinned bundle_idSee Bundle ID
413Payload too heavy (often an uncompressed screenshot)Lower screenshotQuality
429Rate 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:

StatusWarning 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 4xxGeneric [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.

Terminal window
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"
}'