Skip to content

Other SDKs and plain HTTP

Check-ins are a wire format, not an SDK feature. Anything that can POST a check_in envelope item to the ingest endpoint is a supported client, whatever language it is written in.

What is actually verified

Being straight about the difference between "tested" and "should work":

ClientStatus
sentry/sentry (PHP) 4.29Verified against a captured envelope from the real SDK
@sentry/node 10.65Verified against a captured envelope from the real SDK
sentry-sdk (Python) 2.68API confirmed, envelope not captured
Every other Sentry SDKProtocol-compatible in principle, untested here
Hand-rolled HTTPSupported. The format is documented below

The two verified SDKs are pinned in the ingest test suite against real captured bytes, so a protocol regression breaks a build. The rest rely on the SDKs emitting the same envelope shape, which they do, but nobody here has run them end to end.

If you wire up a client not on this list, do the failure test from the quickstart rather than trusting a green first check-in.

Python

bash
pip install sentry-sdk
python
import sentry_sdk

sentry_sdk.init(
    dsn="https://a1b2c3d4e5f60718293a4b5c6d7e8f90@ingest.parsemend.com/42",
    environment="production",
    release="1.4.2",
)

@sentry_sdk.monitor(
    monitor_slug="nightly-reports",
    monitor_config={
        "schedule": {"type": "crontab", "value": "0 3 * * *"},
        "checkin_margin": 10,
        "max_runtime": 60,
        "timezone": "Europe/Sofia",
    },
)
def build_reports():
    ...

sentry_sdk.monitor works as a decorator or a context manager, and sends in_progress before and ok or error after. On Celery, put it below Celery's own @app.task decorator.

Unlike the PHP and JavaScript SDKs, Python's monitor_config uses the wire field names directly: checkin_margin and max_runtime, not camelCase.

For manual control:

python
from sentry_sdk.crons import capture_checkin
from sentry_sdk.crons.consts import MonitorStatus

check_in_id = capture_checkin(
    monitor_slug="inventory-sync",
    status=MonitorStatus.IN_PROGRESS,
    monitor_config={"schedule": {"type": "interval", "value": 15, "unit": "minute"}},
)

capture_checkin(
    monitor_slug="inventory-sync",
    check_in_id=check_in_id,
    status=MonitorStatus.OK,
    duration=elapsed_seconds,
)

Plain HTTP

No SDK required. A check-in is one POST.

bash
DSN_KEY=a1b2c3d4e5f60718293a4b5c6d7e8f90
PROJECT_ID=42
CHECK_IN_ID=$(uuidgen | tr -d '-' | tr 'A-Z' 'a-z')

curl -s -X POST \
  "https://ingest.parsemend.com/api/${PROJECT_ID}/envelope/" \
  -H "X-Sentry-Auth: Sentry sentry_key=${DSN_KEY}, sentry_version=7" \
  -H "Content-Type: application/x-sentry-envelope" \
  --data-binary @- <<EOF
{}
{"type":"check_in"}
{"check_in_id":"${CHECK_IN_ID}","monitor_slug":"backup-to-s3","status":"in_progress","monitor_config":{"schedule":{"type":"crontab","value":"0 4 * * *"},"checkin_margin":10,"max_runtime":45,"timezone":"UTC"}}
EOF

Three lines, newline-separated, in this order:

  1. Envelope header. May be empty ({}). If you cannot set the X-Sentry-Auth header, put your DSN here instead as {"dsn":"https://KEY@ingest.parsemend.com/42"} and it authenticates from that. A ?sentry_key=KEY query parameter also works.
  2. Item header. {"type":"check_in"} is enough. An optional length key makes the payload length-prefixed instead of newline-delimited, which you only need if the payload can contain a raw newline. It cannot here.
  3. Payload. The check-in itself, as one line of JSON.

A successful call returns 200 with {"id":"..."}.

That 200 means accepted, not processed. Ingest queues the envelope and a worker digests it a moment later, so a monitor appears in the panel within a second or two rather than instantly.

Then, when the job finishes, post the terminal check-in with the same check_in_id:

bash
curl -s -X POST \
  "https://ingest.parsemend.com/api/${PROJECT_ID}/envelope/" \
  -H "X-Sentry-Auth: Sentry sentry_key=${DSN_KEY}, sentry_version=7" \
  --data-binary @- <<EOF
{}
{"type":"check_in"}
{"check_in_id":"${CHECK_IN_ID}","monitor_slug":"backup-to-s3","status":"ok","duration":812.4}
EOF

Wrapping that in a shell function and calling it from a crontab entry is a legitimate way to monitor a job in a language with no Sentry SDK at all.

bash
0 4 * * * /usr/local/bin/monitored-backup.sh

Responses worth handling

StatusMeaning
200Accepted and queued
401The key is wrong, or revoked
404No project with that id
413Body over the size cap. Not reachable with a check-in
429Rate limited. Retry-After says for how long

A check-in script should not fail the job it wraps because ingest returned an error. Log it and carry on: a monitoring failure is not a backup failure, and the missed check-in will tell you about it anyway.

What Parsemend does not accept

Sentry has a simplified cron endpoint, /api/{project}/cron/{slug}/{status}/, meant for exactly the curl-from-crontab case above. Parsemend does not implement it. Only /api/{id}/envelope/ and /api/{id}/store/ exist, so a request to the short cron URL gets a 404.

If you find a snippet online that hits that URL, it needs converting to the envelope form above. Anything going through an actual SDK is unaffected, since the SDKs all use envelopes.

Adding a client to the verified list

The verification path is a captured envelope: run the real SDK against a local sink, keep the bytes, and pin them in the ingest test suite. That is how the PHP and JavaScript clients got there, and it is what makes "supported" mean something. Open an issue on the app repository if you want a client added.

Parsemend, by MAVA Design