Files
simstudioai--sim/apps/sim/lib/api/contracts/webhooks.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

296 lines
7.7 KiB
TypeScript

import { z } from 'zod'
import { defineRouteContract } from '@/lib/api/contracts/types'
export const webhooksRouteQuerySchema = z.object({
workflowId: z.string().optional(),
blockId: z.string().optional(),
})
export const webhooksByBlockQuerySchema = z.object({
workflowId: z.string().min(1),
blockId: z.string().min(1),
})
export const webhookProviderConfigSchema = z.record(z.string(), z.unknown())
export const webhookDataSchema = z
.object({
id: z.string(),
path: z.string().optional(),
providerConfig: webhookProviderConfigSchema.optional(),
isActive: z.boolean().optional(),
})
.passthrough()
export type WebhookData = z.output<typeof webhookDataSchema>
export const webhookListItemSchema = z.object({
webhook: webhookDataSchema,
workflow: z
.object({
id: z.string(),
name: z.string(),
})
.optional(),
})
export type WebhookListItem = z.output<typeof webhookListItemSchema>
export const webhookUpsertBodySchema = z
.object({
workflowId: z.string().optional(),
path: z.string().optional(),
provider: z.string().optional(),
providerConfig: webhookProviderConfigSchema.optional(),
blockId: z.string().optional(),
})
.strict()
export type WebhookUpsertBody = z.input<typeof webhookUpsertBodySchema>
export const webhookIdParamsSchema = z.object({
id: z.string().min(1),
})
export const webhookPatchBodySchema = z
.object({
isActive: z.boolean().optional(),
failedCount: z
.number({ error: 'failedCount must be a number' })
.finite('failedCount must be a valid number')
.int('failedCount must be an integer')
.min(0, 'failedCount must be at least 0')
.optional(),
})
.strict()
export type WebhookPatchBody = z.input<typeof webhookPatchBodySchema>
export const webhookPollingParamsSchema = z.object({
provider: z.string().min(1),
})
export const webhookTriggerParamsSchema = z.object({
path: z.string().min(1),
})
export const webhookSvixHeadersSchema = z.object({
'svix-id': z.string().min(1),
'svix-timestamp': z.string().min(1),
'svix-signature': z.string().min(1),
})
export const agentMailEnvelopeSchema = z
.object({
event_type: z.string(),
message: z.unknown().optional(),
})
.passthrough()
const agentMailAttachmentSchema = z
.object({
attachment_id: z.string(),
filename: z.string(),
content_type: z.string(),
size: z.number(),
inline: z.boolean().optional(),
})
.passthrough()
export const agentMailMessageSchema = z
.object({
message_id: z.string(),
thread_id: z.string(),
inbox_id: z.string(),
organization_id: z.string().optional(),
from: z.string(),
to: z.array(z.string()),
cc: z.array(z.string()).optional(),
bcc: z.array(z.string()).optional(),
reply_to: z.array(z.string()).optional(),
subject: z.string().optional(),
preview: z.string().optional(),
text: z.string().nullable().optional(),
html: z.string().nullable().optional(),
attachments: z.array(agentMailAttachmentSchema).optional(),
in_reply_to: z.string().optional(),
references: z.array(z.string()).optional(),
labels: z.array(z.string()).optional(),
sort_key: z.string().optional(),
updated_at: z.string().optional(),
created_at: z.string(),
})
.passthrough()
export const listWebhooksByBlockResponseSchema = z.object({
webhooks: z.array(webhookListItemSchema),
})
export type ListWebhooksByBlockResponse = z.output<typeof listWebhooksByBlockResponseSchema>
const listWebhooksResponseSchema = z.object({
webhooks: z.array(webhookListItemSchema),
})
const upsertWebhookResponseSchema = z.object({
webhook: webhookDataSchema,
})
const getWebhookResponseSchema = z.object({
webhook: webhookListItemSchema,
})
const updateWebhookResponseSchema = z.object({
webhook: webhookDataSchema,
})
const deleteWebhookResponseSchema = z.object({
success: z.literal(true),
})
const webhookPollingResponseSchema = z
.object({
success: z.boolean(),
message: z.string(),
requestId: z.string(),
status: z.enum(['skip', 'completed']).optional(),
total: z.number().optional(),
successful: z.number().optional(),
failed: z.number().optional(),
error: z.string().optional(),
})
.passthrough()
export const listWebhooksByBlockContract = defineRouteContract({
method: 'GET',
path: '/api/webhooks',
query: webhooksByBlockQuerySchema,
response: {
mode: 'json',
schema: listWebhooksByBlockResponseSchema,
},
})
export const listWebhooksContract = defineRouteContract({
method: 'GET',
path: '/api/webhooks',
query: webhooksRouteQuerySchema,
response: {
mode: 'json',
schema: listWebhooksResponseSchema,
},
})
export const upsertWebhookContract = defineRouteContract({
method: 'POST',
path: '/api/webhooks',
body: webhookUpsertBodySchema,
response: {
mode: 'json',
schema: upsertWebhookResponseSchema,
},
})
export const getWebhookContract = defineRouteContract({
method: 'GET',
path: '/api/webhooks/[id]',
params: webhookIdParamsSchema,
response: {
mode: 'json',
schema: getWebhookResponseSchema,
},
})
export const updateWebhookContract = defineRouteContract({
method: 'PATCH',
path: '/api/webhooks/[id]',
params: webhookIdParamsSchema,
body: webhookPatchBodySchema,
response: {
mode: 'json',
schema: updateWebhookResponseSchema,
},
})
export const deleteWebhookContract = defineRouteContract({
method: 'DELETE',
path: '/api/webhooks/[id]',
params: webhookIdParamsSchema,
response: {
mode: 'json',
schema: deleteWebhookResponseSchema,
},
})
export const webhookPollingContract = defineRouteContract({
method: 'GET',
path: '/api/webhooks/poll/[provider]',
params: webhookPollingParamsSchema,
response: {
mode: 'json',
schema: webhookPollingResponseSchema,
},
})
/**
* Webhook trigger endpoints proxy provider responses back to the caller. The
* payload shape varies per provider (challenge text, queued execution result,
* pre-deployment verification, etc.) so the response is genuinely unbounded
* and stays as `z.unknown()`.
*/
export const webhookTriggerGetContract = defineRouteContract({
method: 'GET',
path: '/api/webhooks/trigger/[path]',
params: webhookTriggerParamsSchema,
response: {
mode: 'json',
// untyped-response: webhook trigger forwards arbitrary provider verification or workflow execution payloads
schema: z.unknown(),
},
})
export const webhookTriggerPostContract = defineRouteContract({
method: 'POST',
path: '/api/webhooks/trigger/[path]',
params: webhookTriggerParamsSchema,
response: {
mode: 'json',
// untyped-response: webhook trigger forwards arbitrary provider challenge or workflow execution payloads
schema: z.unknown(),
},
})
/**
* TikTok app-level webhook ingress. Signature is verified from the raw body
* before this schema runs; `content` remains a JSON string per TikTok docs.
*/
export const tiktokWebhookEnvelopeSchema = z.object({
client_key: z.string().min(1).max(255),
event: z.string().min(1).max(255),
create_time: z.number().int().nonnegative(),
user_openid: z.string().min(1).max(255),
content: z.string().max(1_000_000),
})
export type TikTokWebhookEnvelope = z.input<typeof tiktokWebhookEnvelopeSchema>
export const tiktokWebhookHeadersSchema = z.object({
'tiktok-signature': z.string().min(1),
})
export const tiktokWebhookResponseSchema = z.union([
z.object({ ok: z.literal(true) }),
z.object({ error: z.string().min(1) }),
])
export const tiktokWebhookContract = defineRouteContract({
method: 'POST',
path: '/api/webhooks/tiktok',
headers: tiktokWebhookHeadersSchema,
// Body is validated after HMAC verification against the raw payload.
body: tiktokWebhookEnvelopeSchema,
response: {
mode: 'json',
schema: tiktokWebhookResponseSchema,
},
})