{"openapi":"3.1.0","info":{"title":"PAD.ai API","version":"1.0.0","description":"Public v1 of the PAD lifecycle marketing platform. All endpoints are workspace-scoped via the `Authorization: Bearer <api_key>` header.","contact":{"name":"PAD support","email":"support@thevisualpay.com"},"license":{"name":"Commercial"}},"servers":[{"url":"https://pad.thevisualpay.com"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pad_api_key"}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found in this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"API key scope is insufficient for this operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Profile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"externalId":{"type":"string"},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"traits":{"type":"object"},"firstSeenAt":{"type":"string","format":"date-time"},"lastSeenAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","externalId","firstSeenAt","createdAt","updatedAt"]},"Event":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"profileId":{"type":"string","format":"uuid"},"properties":{"type":"object"},"timestamp":{"type":"string","format":"date-time"},"source":{"type":"string","nullable":true}}},"Send":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","sent","delivered","bounced","complained","failed","suppressed"]},"template":{"type":"string"},"recipient":{"type":"string"},"subject":{"type":"string","nullable":true},"provider":{"type":"string","nullable":true},"providerMessageId":{"type":"string","nullable":true},"timestamps":{"type":"object","properties":{"created":{"type":"string","format":"date-time"},"sent":{"type":"string","format":"date-time","nullable":true},"delivered":{"type":"string","format":"date-time","nullable":true},"opened":{"type":"string","format":"date-time","nullable":true},"clicked":{"type":"string","format":"date-time","nullable":true},"bounced":{"type":"string","format":"date-time","nullable":true},"complained":{"type":"string","format":"date-time","nullable":true}}},"error":{"type":"string","nullable":true}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","nullable":true}}}}}}},"security":[{"bearerAuth":[]}],"paths":{"/v1/profiles":{"post":{"summary":"Upsert a profile","operationId":"upsertProfile","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["externalId"],"properties":{"externalId":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"traits":{"type":"object"}}}}}},"responses":{"200":{"description":"Profile upserted","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"externalId":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/profiles/{id}":{"get":{"summary":"Get a profile by id or external id","operationId":"getProfile","parameters":[{"name":"id","in":"path","required":true,"description":"UUID (matches profiles.id) or external id","schema":{"type":"string"}}],"responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"summary":"Permanently delete a profile and all linked data (GDPR right-to-erasure)","operationId":"deleteProfile","description":"Cascades through events, sends, consents, campaign_runs, and ai_memories tied to this profile. Idempotent — calling twice returns 404 on the second call. Requires an API key with `server` or `admin` scope. Writes a `profile.deleted` row to the audit log.","parameters":[{"name":"id","in":"path","required":true,"description":"UUID (matches profiles.id) or external id","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/templates/preview":{"post":{"summary":"Render a template + variables to HTML without sending","operationId":"previewTemplate","description":"Resolves the workspace's DB-authored template first; falls back to the code-defined template registry. The `source` field in the response tells you which path matched.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["template"],"properties":{"template":{"type":"string","description":"Template slug"},"variables":{"type":"object","description":"Variables passed to the renderer"}}}}}},"responses":{"200":{"description":"Rendered template","content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","enum":["db","code"]},"subject":{"type":"string"},"preheader":{"type":"string","nullable":true},"html":{"type":"string"},"text":{"type":"string"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/export/profiles.csv":{"get":{"summary":"Stream a CSV of every profile in the workspace","operationId":"exportProfilesCsv","description":"Server-scope or admin-scope key required. Streams text/csv; no file size cap. Header row: `external_id,email,phone,first_seen_at,last_seen_at,traits`.","responses":{"200":{"description":"CSV stream","content":{"text/csv":{"schema":{"type":"string"}}}},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/export/events.csv":{"get":{"summary":"Stream a CSV of every event in the workspace","operationId":"exportEventsCsv","description":"Server-scope or admin-scope key required. Header row: `id,profile_external_id,type,timestamp,properties_json`. Optional `since` + `until` ISO-8601 query params bound the window.","parameters":[{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"CSV stream","content":{"text/csv":{"schema":{"type":"string"}}}},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/events":{"post":{"summary":"Track an event (single, real-time, triggers campaigns)","operationId":"trackEvent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event","externalId"],"properties":{"event":{"type":"string","maxLength":200},"externalId":{"type":"string"},"properties":{"type":"object"},"timestamp":{"type":"string","format":"date-time"},"idempotencyKey":{"type":"string","maxLength":200}}}}}},"responses":{"200":{"description":"Event tracked","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"idempotent":{"type":"boolean"}}}}}}}}},"/v1/events/bulk":{"post":{"summary":"Bulk ingest events (up to 500, no campaign enqueue)","operationId":"trackEventsBulk","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["events"],"properties":{"events":{"type":"array","minItems":1,"maxItems":500,"items":{"$ref":"#/components/schemas/Event"}}}}}}},"responses":{"200":{"description":"Bulk result","content":{"application/json":{"schema":{"type":"object","properties":{"succeeded":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object"}}}}}}}}}},"/v1/events/list":{"get":{"summary":"List events (cursor-paginated, DESC by timestamp)","operationId":"listEvents","parameters":[{"name":"profileId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"externalId","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string"}},{"name":"cursor","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200,"default":50}}],"responses":{"200":{"description":"Page of events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"nextCursor":{"type":"string","nullable":true}}}}}}}}},"/v1/sends":{"post":{"summary":"Send a transactional or marketing email","operationId":"sendEmail","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["template","to"],"properties":{"template":{"type":"string"},"to":{"type":"string","description":"Profile externalId"},"variables":{"type":"object"},"idempotencyKey":{"type":"string"},"from":{"type":"string","description":"Full From override, e.g. \"Acme <noreply@mail.acme.com>\"."},"fromName":{"type":"string","maxLength":200,"description":"Override only the From display name; the address stays your verified sending domain. Ignored when `from` is set."},"replyTo":{"type":"string","format":"email"},"tags":{"type":"array","maxItems":20,"items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"Send queued or completed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"sendId":{"type":"string","format":"uuid"},"status":{"type":"string"},"providerMessageId":{"type":"string","nullable":true},"reason":{"type":"string","nullable":true}}}}}}}}},"/v1/sends/{id}":{"get":{"summary":"Get a send by id","operationId":"getSend","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Send","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Send"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/sends/list":{"get":{"summary":"List sends (cursor-paginated, DESC by createdAt)","operationId":"listSends","parameters":[{"name":"profileId","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"template","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","sent","delivered","bounced","complained","failed","suppressed"]}},{"name":"cursor","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200,"default":50}}],"responses":{"200":{"description":"Page of sends","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Send"}},"nextCursor":{"type":"string","nullable":true}}}}}}}}},"/v1/consents":{"post":{"summary":"Record a profile consent","operationId":"setConsent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["externalId","channel","category","granted"],"properties":{"externalId":{"type":"string"},"channel":{"type":"string","enum":["email","sms","push"]},"category":{"type":"string"},"granted":{"type":"boolean"},"source":{"type":"string"}}}}}},"responses":{"200":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}