Backend Events
Send durable server-side product events to SafetyKit.
Beta.
SafetyKit uses backend events to understand meaningful product actions such as account creation, account updates, user-to-user contact, content uploads, user reports, and moderation decisions. Send events from your backend after the action has happened in your system. Do not send backend events directly from a browser or mobile client.
Use the Events API reference for the full request schema, SDK examples, and response fields.
Quick Start
Section titled “Quick Start”Send one event, or an ordered batch of events, to:
POST https://api.safetykit.com/v1/eventsAuthorization: Bearer <SAFETYKIT_API_KEY>Content-Type: application/jsonSingle event:
{ "type": "user_contact", "event_name": "message_sent", "user_id": "user_123", "target_user_id": "user_456", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "message_abc123", "content": [ { "type": "text", "key": "body", "text": "Hey, is this still available?" } ], "metadata": { "conversation_id": "conversation_789", "channel": "marketplace_dm" }}Response:
{ "status": "ok", "event_id": "01KSR4C8JN1SA9X63T6P368K6W"}Batch event request:
[ { "type": "update_account", "event_name": "profile_updated", "user_id": "user_123", "timestamp": "2026-05-21T00:15:10.000Z", "metadata": { "changed_fields": ["bio"] } }, { "type": "user_contact", "event_name": "message_sent", "user_id": "user_123", "target_user_id": "user_456", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "message_abc123" }]Response:
{ "status": "ok", "event_ids": ["01KSR4C8JN1SA9X63T6P368K6W", "01KSR4CB1MFVX1TAYM46YQXZ3E"]}SafetyKit ingests every event in a batch before returning a response.
Event Shape
Section titled “Event Shape”Every event is a JSON object:
{ "type": "user_contact", "event_name": "message_sent", "user_id": "user_123", "target_user_id": "user_456", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "message_abc123", "content": [], "resources_used": [], "metadata": {}}Required base fields:
type: one ofuser_contact,content_uploaded,create_account,update_account,user_report, ormoderation_decisionevent_name: a stable, low-cardinality snake_case name for the product actiontimestamp: the time the event occurred in your system, as an ISO 8601 datetime stringuser_id: your canonical stable identifier for the user or account performing the action. Foruser_report, this is the reporter. Do not senduser_idformoderation_decision.
Use stable event names like message_sent or profile_updated. Put dynamic values in typed fields, content, resources_used, or metadata.
Common optional fields:
target_user_id: the other user in a user-to-user interaction, the user being reported foruser_report, or the user being moderated formoderation_decisiontarget_content_id: foruser_reportormoderation_decision, your stable identifier for the content being reported or moderatedcontent_id: your stable identifier for the content involved in the event, such as a message, listing, page, post, profile, uploaded media item, or report objectcontent: user-authored or user-uploaded content SafetyKit should compare or analyzeresources_used: reusable identifiers or real-world resources associated with the eventmetadata: freeform non-PII product context for filtering, segmentation, debugging, and investigation
Include content_id whenever you send content and your system has a stable ID for that content. SafetyKit stores this alongside the computed content hash so you can later report, search, join, or reconcile the content in your system.
Event Types
Section titled “Event Types”user_contact
Section titled “user_contact”Use when one user contacts, transacts with, or otherwise interacts with another user.
Required fields:
user_idtarget_user_id
Recommended fields:
content, if the interaction includes a message or user-authored notecontent_id, if the interaction includes a message or other stable content objectmetadata, for conversation IDs, transaction IDs, channel names, or similar context
Example:
{ "type": "user_contact", "event_name": "message_sent", "user_id": "sender_123", "target_user_id": "recipient_456", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "message_abc123", "content": [ { "type": "text", "key": "body", "text": "Hey, is this still available?" } ], "metadata": { "conversation_id": "conversation_789" }}create_account
Section titled “create_account”Use when a user creates an account.
Required fields:
user_id
Recommended fields:
resources_used, for signup emails, phones, names, addresses, URLs, or social handlescontent, for public bio or profile textcontent_id, if you include profile, bio, media, or other content with a stable ID in your systemmetadata, for signup source, cohort, or other event-local context
update_account
Section titled “update_account”Use when a user updates account or profile information.
Required fields:
user_id
Recommended fields:
resources_used, for changed emails, phones, social handles, addresses, URLs, or payment identifierscontent, for changed public bio or profile textcontent_id, if the changed profile, bio, media, or other content has a stable ID in your systemmetadata, for changed field names, visibility state, or other event-local context
Example:
{ "type": "update_account", "event_name": "profile_updated", "user_id": "user_123", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "profile_user_123", "content": [ { "type": "text", "key": "bio", "text": "Independent designer making limited-run products." } ], "metadata": { "changed_fields": ["bio"], "profile_visibility": "public" }}content_uploaded
Section titled “content_uploaded”Use when a user posts, uploads, publishes, edits, or replaces content.
Required fields:
user_idcontent
Recommended fields:
content_id, for the listing, post, page, profile, upload, media item, or other content object being added or changedmetadata, for listing IDs, post IDs, visibility state, category, or similar product context
Example:
{ "type": "content_uploaded", "event_name": "project_story_updated", "user_id": "creator_456", "timestamp": "2026-05-21T00:15:15.000Z", "content_id": "project_789_story", "content": [ { "type": "text", "key": "project_story", "text": "We are building a new limited edition product." }, { "type": "image", "key": "hero_image", "source": { "type": "url", "url": "https://cdn.example.com/project/hero.jpg" } } ], "metadata": { "project_id": "project_789", "visibility": "public" }}user_report
Section titled “user_report”Use when a user reports another user or a piece of content.
Required fields:
user_idlabels
Recommended fields:
target_user_id, for the user being reportedtarget_content_id, for the content being reportedcontent_id, for your stable identifier for the report objectcontent, for report text, screenshots, evidence, or other explanatory content SafetyKit should compare or analyzemetadata, for report surface, source, queue, or other product context
A report should include target_user_id, target_content_id, or both. Prefer both when the report is about a content item and you know the user associated with that content.
Example:
{ "type": "user_report", "event_name": "content_report_submitted", "user_id": "reporter_789", "target_user_id": "seller_456", "target_content_id": "listing_abc123", "labels": ["spam"], "content_id": "report_abc123", "timestamp": "2026-05-21T00:15:15.000Z", "content": [ { "type": "text", "key": "report_reason", "text": "This listing looks like spam." } ], "metadata": { "report_surface": "listing_page" }}SafetyKit stores the canonical report event and records one label row per report label.
moderation_decision
Section titled “moderation_decision”Use when an automated enforcement system or manual review workflow labels or decides on a user or piece of content.
Required fields:
source_typesource_idlabels
Recommended fields:
target_user_id, for the user being moderatedtarget_content_id, for the content being moderatedcontent_id, for your stable identifier for the decision objectcontent, for review notes, decision rationale, evidence, or other explanatory content SafetyKit should compare or analyzemetadata, for outcome, confidence, queue, rule, model version, or other product context
A moderation decision should include target_user_id, target_content_id, or both. Do not include user_id; use source_type and source_id for attribution.
source_type is the source category for the decision. Supported values:
human_moderator: a human reviewer in your moderation workflowexpert_labeler: an expert human labeler or specialist reviewerautomation: an automated model, rules engine, or enforcement workflowvendor: an external review or moderation provider
source_id is the stable identifier within source_type. Examples: moderator@example.com or 550e8400-e29b-41d4-a716-446655440000 for expert_labeler or human_moderator, automated_review_v2026_06_04 for automation, or <vendor_name>-v2.1 for vendor.
Example:
{ "type": "moderation_decision", "event_name": "content_moderation_decided", "source_type": "automation", "source_id": "automated_review_v2026_06_04", "target_user_id": "seller_456", "target_content_id": "listing_abc123", "labels": ["spam"], "content_id": "decision_abc123", "timestamp": "2026-05-21T00:15:15.000Z", "metadata": { "decision": "remove", "confidence": "high" }}Content
Section titled “Content”Use content for user-authored or user-uploaded content SafetyKit should compare or analyze.
Text part:
{ "type": "text", "key": "body", "text": "Hey, is this still available?"}Image part:
{ "type": "image", "key": "profile_photo", "source": { "type": "url", "url": "https://cdn.example.com/images/profile.jpg" }}Guidelines:
- Split structured content into meaningful parts.
- Use stable
keyvalues such asbody,title,description,bio, orprimary_photo. - Include the top-level
content_idwhenever the content has a stable ID in your system. - Do not add IDs or timestamps to text just to make it unique.
- Send original user-authored text rather than rendered HTML when possible.
- Send canonical media URLs when available.
Resources
Section titled “Resources”Use resources_used for reusable identifiers associated with the event.
[ { "type": "email", "value": "user@example.com" }, { "type": "phone", "value": "+12069406843" }, { "type": "url", "value": "https://example.com/profile/user_123" }]Common resource types include email, phone, name, address, url, facebook, instagram, x, tiktok, youtube, linkedin, and payment_instrument.
Guidelines:
- Send values exactly as your backend receives or stores them.
- Prefer E.164 phone numbers if your system already has them.
- Prefer canonical URLs if your system already has them.
- Do not put multiple resources in one string.
- Do not duplicate the same value in both
resources_usedandmetadata.
Metadata
Section titled “Metadata”Use metadata for freeform non-PII product context for filtering, segmentation, debugging, and investigation.
{ "conversation_id": "conversation_789", "channel": "marketplace_dm", "is_first_message": true, "message_count_24h": 12}Metadata values may be strings, numbers, booleans, or arrays of strings, numbers, or booleans. Metadata keys should be stable snake_case strings.
Do not put raw PII, message bodies, bios, addresses, image URLs, or reusable identifiers in metadata. Use content or resources_used.
Operational Guidance
Section titled “Operational Guidance”- Send events after durable writes in your own system.
- For high-volume systems, write events to your own outbox or queue and have a worker send them to SafetyKit.
- Retry transient network errors and 5xx responses with exponential backoff.
- Do not retry 4xx responses without changing the request.
- Store your own source event identifier in
metadataif you need replay or reconciliation.