Overview
Raw payloads storage lets you capture the exact JSON payloads received from wearable providers before any processing. This is useful when you need to:
- Debug data issues - inspect exactly what a provider sent when something doesn’t look right
- Test ingestion changes - replay real payloads against updated parsing logic to make sure nothing breaks
- Reproduce bugs - grab the original payload that caused a problem and use it in local development
The feature is disabled by default and adds zero overhead when turned off.
Configuration
Add these environment variables to your .env file:
Raw payloads can contain sensitive health data. Make sure your storage destination meets your organization’s data protection requirements.
Storage Backends
Disabled (default)
No payloads are stored. The store_raw_payload() call returns immediately with no overhead.
Log
Writes each payload as a structured JSON line to stdout. Useful for development, or when your infrastructure already captures application logs (e.g., CloudWatch, Datadog).
Example output:
Uploads each payload as a JSON file to an S3 bucket (or any S3-compatible object store like MinIO or Railway Object Storage).
AWS credentials are reused from the existing application config (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION). See the AWS Setup guide for details.
Files are organized by provider, source, date, and user:
Example:
_unknown is used when a user ID is not available (e.g., Garmin webhooks)
file_id is a random 12-character hex string
Each object includes metadata for filtering and querying:
Supported Ingestion Points
Raw payloads storage is currently enabled at two ingestion points:
SDK Payload Offload
By default the SDK sync endpoint hands the whole payload to Celery, which keeps it in the Redis broker. A queued backlog then grows with payload size, and a large backfill can push Redis to maxmemory.
SDK_PAYLOAD_S3_OFFLOAD uploads the payload to S3 instead and queues only its s3://bucket/key reference. The worker fetches the body when the task runs.
It needs a bucket (RAW_PAYLOAD_S3_BUCKET, or AWS_BUCKET_NAME) and startup fails without one. It does not need RAW_PAYLOAD_STORAGE=s3 — archival is a debugging aid and must not gate queue reliability, so offload works with archival disabled. For the same reason RAW_PAYLOAD_MAX_SIZE_BYTES does not apply to it.
With RAW_PAYLOAD_STORAGE=s3 there is still one object, written to the same key with the same metadata, serving as both the queued payload and the archive.
With RAW_PAYLOAD_STORAGE=disabled the object only exists to carry the payload, so the worker deletes it once the batch is committed — a failed batch keeps its payload for diagnosis. This needs s3:DeleteObject on the bucket, otherwise every batch logs a delete failure.
S3 is now on the critical path. Keep any lifecycle expiry above your worst queue lag, and expect 503 from the endpoint when the upload fails.
Error Handling
Raw payloads storage is designed to never break your data ingestion pipeline:
- Oversized payloads - Payloads exceeding
RAW_PAYLOAD_MAX_SIZE_BYTES are skipped with a warning log. Data processing continues normally.
- S3 upload failures - Errors are logged but not propagated. The webhook or sync request continues processing.
- Missing S3 bucket - If the S3 backend is configured but no bucket is available, it falls back to
disabled with an error log.
- S3 client failure - If the S3 client cannot be created (e.g., missing
boto3 or invalid credentials), it falls back to disabled.
This covers archival only. Under SDK_PAYLOAD_S3_OFFLOAD the upload is the payload’s only copy, so a failure returns 503 and the SDK retries, rather than a batch being queued without a body.
Example Setup
Development (Log)
Development (MinIO)
Production (S3)
Then inspect payloads in Docker logs: MinIO is an S3-compatible object store you can run locally. This lets you browse stored payloads via a web UI without needing an AWS account.1. Start MinIO (on the same Docker network as the app):Use --hostname minio to register a clean DNS name in the Docker network. Boto3 rejects hostnames with special characters (e.g. double underscores), so container names like minio__open-wearables won’t work as endpoint URLs.
2. Create the bucket - open the MinIO Console at http://localhost:9003 (login minioadmin / minioadmin) and create a bucket called raw-payloads, or use the CLI:3. Configure env vars in backend/config/.env:Payloads will appear in the MinIO Console under the raw-payloads bucket.
Replaying Stored Payloads
Use the replay_raw_payloads.py script to re-send S3-stored payloads through the SDK sync endpoint. This is useful for reproducing issues, re-processing data after a fix, or load testing.
The script reads AWS/S3 credentials from environment variables automatically.
Filtering
Narrow down which payloads to replay:
Replaying to a different user
Use --target-user-id to send one user’s payloads to a different user account:
Dry run
Preview which payloads would be sent without making any requests:
Options