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":
| Client | Status |
|---|---|
sentry/sentry (PHP) 4.29 | Verified against a captured envelope from the real SDK |
@sentry/node 10.65 | Verified against a captured envelope from the real SDK |
sentry-sdk (Python) 2.68 | API confirmed, envelope not captured |
| Every other Sentry SDK | Protocol-compatible in principle, untested here |
| Hand-rolled HTTP | Supported. 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
pip install sentry-sdkimport 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:
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.
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"}}
EOFThree lines, newline-separated, in this order:
- Envelope header. May be empty (
{}). If you cannot set theX-Sentry-Authheader, put your DSN here instead as{"dsn":"https://KEY@ingest.parsemend.com/42"}and it authenticates from that. A?sentry_key=KEYquery parameter also works. - Item header.
{"type":"check_in"}is enough. An optionallengthkey makes the payload length-prefixed instead of newline-delimited, which you only need if the payload can contain a raw newline. It cannot here. - 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:
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}
EOFWrapping 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.
0 4 * * * /usr/local/bin/monitored-backup.shResponses worth handling
| Status | Meaning |
|---|---|
200 | Accepted and queued |
401 | The key is wrong, or revoked |
404 | No project with that id |
413 | Body over the size cap. Not reachable with a check-in |
429 | Rate 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.
