Prompt

I’m building a multi-tenant app and need per-customer webhook delivery logs

Technology / API Platforms2 observationsLast seen Nov 7, 2026

Latest observation

Nov 7, 2026OpenAI APIWeb search: off

A good pattern for per-customer webhook delivery logs in a multi-tenant app is to treat webhook delivery as its own first-class entity, with every record scoped to a tenant/customer.

Core ideas

1) Store each delivery attempt

Keep a row for every webhook attempt, not just every event. This lets you track retries, failures, and final outcomes.

Typical fields:

  • id
  • tenant_id / customer_id
  • event_id (the business event being sent)
  • subscription_id or endpoint_id
  • delivery_status (pending, success, failed, retrying)
  • attempt_number
  • http_status
  • response_body or truncated response snippet
  • request_headers
  • request_payload or payload reference
  • started_at
  • finished_at
  • next_retry_at
  • error_message
  • signature_version
  • created_at

2) Separate event from delivery attempt

A single event may go to multiple endpoints, and each endpoint may retry multiple times.

Suggested structure:

  • webhook_events: one business event generated by your app
  • webhook_deliveries: one row per endpoint/customer target per event
  • webhook_attempts: one row per retry attempt, if you want detailed history

If you want simplicity, you can store attempts directly in one webhook_deliveries table with a attempt_count and append-only attempt metadata. If you need auditing/debugging, a separate webhook_attempts table is better.

Multi-tenant scoping

Every table should include:

  • tenant_id
  • indexes starting with tenant_id
  • authorization checks that always filter by tenant_id

Example indexes:

  • (tenant_id, created_at desc)
  • (tenant_id, delivery_status, created_at desc)
  • (tenant_id, endpoint_id, created_at desc)

If tenants can only view their own logs, enforce this at:

  • application layer
  • database row-level security if available

Recommended schema example

webhook_events

  • id
  • tenant_id
  • event_type
  • resource_type
  • resource_id
  • payload_json
  • created_at

webhook_endpoints

  • id
  • tenant_id
  • url
  • secret
  • is_active
  • created_at

webhook_deliveries

  • id
  • tenant_id
  • event_id
  • endpoint_id
  • status
  • attempt_count
  • last_http_status
  • last_error
  • last_attempt_at
  • next_retry_at
  • created_at
  • updated_at

webhook_attempts

  • id
  • tenant_id
  • delivery_id
  • attempt_number
  • request_sent_at
  • response_received_at
  • http_status
  • error_message
  • response_body
  • duration_ms

Operational tips

Keep payloads manageable

Store:

  • full payload if small, or
  • compressed payload, or
  • object storage pointer

For logs, often it’s enough to store:

  • payload hash
  • a truncated response
  • headers
  • error summary

Add retention policies

Webhook logs can grow fast. Common approach:

  • keep detailed attempts for 30–90 days
  • keep summary rows longer
  • archive old records to cheaper storage

Consider privacy/security

Webhook payloads may include PII or secrets.

  • redact sensitive fields
  • encrypt at rest
  • avoid storing secrets in plaintext
  • limit who can view logs

Build tenant-safe querying

All log queries should be tenant-scoped:

  • WHERE tenant_id = ?
  • no cross-tenant joins without explicit admin access

UI ideas

For each customer, show:

  • list of recent webhook events
  • delivery status
  • endpoint target
  • retry count
  • last error
  • timestamps
  • drill-down into attempt history

If you want the simplest workable design

Start with just two tables:

  1. webhook_events
  2. webhook_deliveries

Add webhook_attempts later if retries/auditing become important.

If you want, I can give you:

  • a concrete SQL schema,
  • a Prisma/Sequelize/Django model version,
  • or an API design for exposing these logs per tenant.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (2 observations for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.