Skip to content
StatusSupportDashboard
Backend Events

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.

Send one event, or an ordered batch of events, to:

Terminal window
POST https://api.safetykit.com/v1/events
Authorization: Bearer <SAFETYKIT_API_KEY>
Content-Type: application/json

Single 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.

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 of user_contact, content_uploaded, create_account, update_account, user_report, or moderation_decision
  • event_name: a stable, low-cardinality snake_case name for the product action
  • timestamp: the time the event occurred in your system, as an ISO 8601 datetime string
  • user_id: your canonical stable identifier for the user or account performing the action. For user_report, this is the reporter. Do not send user_id for moderation_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 for user_report, or the user being moderated for moderation_decision
  • target_content_id: for user_report or moderation_decision, your stable identifier for the content being reported or moderated
  • content_id: your stable identifier for the content involved in the event, such as a message, listing, page, post, profile, uploaded media item, or report object
  • content: user-authored or user-uploaded content SafetyKit should compare or analyze
  • resources_used: reusable identifiers or real-world resources associated with the event
  • metadata: 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.

Use when one user contacts, transacts with, or otherwise interacts with another user.

Required fields:

  • user_id
  • target_user_id

Recommended fields:

  • content, if the interaction includes a message or user-authored note
  • content_id, if the interaction includes a message or other stable content object
  • metadata, 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"
}
}

Use when a user creates an account.

Required fields:

  • user_id

Recommended fields:

  • resources_used, for signup emails, phones, names, addresses, URLs, or social handles
  • content, for public bio or profile text
  • content_id, if you include profile, bio, media, or other content with a stable ID in your system
  • metadata, for signup source, cohort, or other event-local context

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 identifiers
  • content, for changed public bio or profile text
  • content_id, if the changed profile, bio, media, or other content has a stable ID in your system
  • metadata, 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"
}
}

Use when a user posts, uploads, publishes, edits, or replaces content.

Required fields:

  • user_id
  • content

Recommended fields:

  • content_id, for the listing, post, page, profile, upload, media item, or other content object being added or changed
  • metadata, 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"
}
}

Use when a user reports another user or a piece of content.

Required fields:

  • user_id
  • labels

Recommended fields:

  • target_user_id, for the user being reported
  • target_content_id, for the content being reported
  • content_id, for your stable identifier for the report object
  • content, for report text, screenshots, evidence, or other explanatory content SafetyKit should compare or analyze
  • metadata, 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.

Use when an automated enforcement system or manual review workflow labels or decides on a user or piece of content.

Required fields:

  • source_type
  • source_id
  • labels

Recommended fields:

  • target_user_id, for the user being moderated
  • target_content_id, for the content being moderated
  • content_id, for your stable identifier for the decision object
  • content, for review notes, decision rationale, evidence, or other explanatory content SafetyKit should compare or analyze
  • metadata, 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 workflow
  • expert_labeler: an expert human labeler or specialist reviewer
  • automation: an automated model, rules engine, or enforcement workflow
  • vendor: 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"
}
}

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 key values such as body, title, description, bio, or primary_photo.
  • Include the top-level content_id whenever 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.

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_used and 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.

  • 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 metadata if you need replay or reconciliation.