openapi: 3.1.0 info: title: Send Engine API version: 0.1.0 description: | Send Engine external API and webhooks. servers: - url: https://send-engine.example.com security: - bearerAuth: [] paths: /v1/send-jobs: post: summary: Submit send job (sending proxy) security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubmitSendJobRequest' responses: '200': description: Queued content: application/json: schema: $ref: '#/components/schemas/SubmitSendJobResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Unprocessable Entity (e.g. tenant not found) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '409': description: Idempotency conflict content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Validation error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/send-jobs: post: summary: Create send job (legacy/internal) security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSendJobRequest' responses: '200': description: Created content: application/json: schema: $ref: '#/components/schemas/CreateSendJobResponse' '409': description: Conflict content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Validation error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/send-jobs/{id}: get: summary: Get send job security: - bearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SendJob' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/send-jobs/{id}/cancel: post: summary: Cancel send job security: - bearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SendJobStatusResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /webhooks/subscriptions: post: summary: Member Center subscription events security: - webhookSignature: [] webhookTimestamp: [] webhookNonce: [] webhookClientId: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubscriptionEvent' responses: '200': description: Accepted '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '409': description: Duplicate event content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Validation error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /webhooks/lists/full-sync: post: summary: Member Center full list sync security: - webhookSignature: [] webhookTimestamp: [] webhookNonce: [] webhookClientId: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FullSyncBatch' responses: '200': description: Accepted '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '409': description: Duplicate batch content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '422': description: Validation error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /webhooks/ses: post: summary: SES/SNS events security: - sesSignature: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SesEvent' responses: '200': description: OK '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT webhookSignature: type: apiKey in: header name: X-Signature webhookTimestamp: type: apiKey in: header name: X-Timestamp webhookNonce: type: apiKey in: header name: X-Nonce webhookClientId: type: apiKey in: header name: X-Client-Id sesSignature: type: apiKey in: header name: X-Amz-Sns-Signature schemas: CreateSendJobRequest: type: object required: [list_id, subject] properties: tenant_id: type: string format: uuid list_id: type: string format: uuid name: type: string subject: type: string minLength: 1 body_html: type: string body_text: type: string template: type: object additionalProperties: true scheduled_at: type: string format: date-time window_start: type: string format: date-time window_end: type: string format: date-time tracking: $ref: '#/components/schemas/TrackingOptions' oneOf: - required: [body_html] - required: [body_text] - required: [template] CreateSendJobResponse: type: object required: [send_job_id, status] properties: send_job_id: type: string format: uuid status: type: string enum: [pending, running, completed, failed, cancelled] SendJob: type: object required: [id, tenant_id, list_id, campaign_id, status] properties: id: type: string format: uuid tenant_id: type: string format: uuid list_id: type: string format: uuid campaign_id: type: string format: uuid status: type: string enum: [pending, running, completed, failed, cancelled] scheduled_at: type: string format: date-time window_start: type: string format: date-time window_end: type: string format: date-time SendJobStatusResponse: type: object required: [id, status] properties: id: type: string format: uuid status: type: string enum: [pending, running, completed, failed, cancelled] SubmitSendJobRequest: type: object required: [message_type, from, to, subject, html, text, tags, idempotency_key] properties: message_type: type: string enum: [newsletter, transactional] from: type: string format: email to: type: array items: type: string format: email minItems: 1 subject: type: string minLength: 1 html: type: string text: type: string headers: type: object additionalProperties: type: string list_unsubscribe: $ref: '#/components/schemas/ListUnsubscribe' tags: $ref: '#/components/schemas/MessageTags' idempotency_key: type: string minLength: 1 SubmitSendJobResponse: type: object required: [job_id, status] properties: job_id: type: string format: uuid status: type: string enum: [queued] ListUnsubscribe: type: object required: [url] properties: url: type: string format: uri mailto: type: string format: email MessageTags: type: object required: [campaign_id, site_id, list_id, segment] properties: campaign_id: type: string site_id: type: string list_id: type: string segment: type: string TrackingOptions: type: object properties: open: type: boolean click: type: boolean SubscriptionEvent: type: object required: [event_id, event_type, tenant_id, list_id, subscriber, occurred_at] properties: event_id: type: string format: uuid event_type: type: string enum: [subscription.activated, subscription.unsubscribed, preferences.updated] tenant_id: type: string format: uuid list_id: type: string format: uuid subscriber: $ref: '#/components/schemas/SubscriberPayload' occurred_at: type: string format: date-time SubscriberPayload: type: object required: [id, email, status] properties: id: type: string format: uuid email: type: string format: email status: type: string enum: [active, unsubscribed, bounced, complaint, suppressed] preferences: type: object additionalProperties: true FullSyncBatch: type: object required: [sync_id, batch_no, batch_total, tenant_id, list_id, subscribers, occurred_at] properties: sync_id: type: string format: uuid batch_no: type: integer minimum: 1 batch_total: type: integer minimum: 1 tenant_id: type: string format: uuid list_id: type: string format: uuid subscribers: type: array items: $ref: '#/components/schemas/SubscriberPayload' occurred_at: type: string format: date-time SesEvent: type: object required: [event_type, message_id, tenant_id, email, occurred_at] properties: event_type: type: string enum: [bounce, hard_bounced, soft_bounced, complaint, suppression, delivery, open, click] message_id: type: string tenant_id: type: string format: uuid email: type: string format: email bounce_type: type: string enum: [hard, soft] occurred_at: type: string format: date-time ErrorResponse: type: object required: [error, message, request_id] properties: error: type: string message: type: string request_id: type: string