diff --git a/apps/api/drizzle/0023_sent_content_versions.sql b/apps/api/drizzle/0023_sent_content_versions.sql new file mode 100644 index 0000000..f7e573e --- /dev/null +++ b/apps/api/drizzle/0023_sent_content_versions.sql @@ -0,0 +1,18 @@ +CREATE TABLE "email_content_versions" ( + "id" uuid PRIMARY KEY NOT NULL, + "version_id" text NOT NULL, + "team_id" uuid NOT NULL, + "sequence_email_id" uuid NOT NULL, + "content_hash" text NOT NULL, + "subject" text NOT NULL, + "content" jsonb NOT NULL, + "created_at" timestamp with time zone DEFAULT now(), + CONSTRAINT "email_content_versions_version_id_unique" UNIQUE("version_id"), + CONSTRAINT "email_content_versions_version_id_check" CHECK ("email_content_versions"."version_id" ~ '^ver_') +); +--> statement-breakpoint +ALTER TABLE "email_deliveries" ADD COLUMN "content_version_id" uuid;--> statement-breakpoint +ALTER TABLE "email_content_versions" ADD CONSTRAINT "email_content_versions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "email_content_versions" ADD CONSTRAINT "email_content_versions_sequence_email_id_sequence_emails_id_fk" FOREIGN KEY ("sequence_email_id") REFERENCES "public"."sequence_emails"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "email_content_versions_email_hash_idx" ON "email_content_versions" USING btree ("sequence_email_id","content_hash");--> statement-breakpoint +ALTER TABLE "email_deliveries" ADD CONSTRAINT "email_deliveries_content_version_id_email_content_versions_id_fk" FOREIGN KEY ("content_version_id") REFERENCES "public"."email_content_versions"("id") ON DELETE set null ON UPDATE no action; \ No newline at end of file diff --git a/apps/api/drizzle/meta/0023_snapshot.json b/apps/api/drizzle/meta/0023_snapshot.json new file mode 100644 index 0000000..bf174b5 --- /dev/null +++ b/apps/api/drizzle/meta/0023_snapshot.json @@ -0,0 +1,11058 @@ +{ + "id": "0dead4f4-8efd-42fe-bb7e-10d966e40ab3", + "prevId": "5fecb901-81a8-4cdd-bb15-20feebef020b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.abuse_flags": { + "name": "abuse_flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "flag_id": { + "name": "flag_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "sequence_email_id": { + "name": "sequence_email_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resolved_by": { + "name": "resolved_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_note": { + "name": "resolution_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "abuse_flags_status_created_at_idx": { + "name": "abuse_flags_status_created_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "abuse_flags_organization_kind_status_idx": { + "name": "abuse_flags_organization_kind_status_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "abuse_flags_organization_id_organizations_id_fk": { + "name": "abuse_flags_organization_id_organizations_id_fk", + "tableFrom": "abuse_flags", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "abuse_flags_team_id_teams_id_fk": { + "name": "abuse_flags_team_id_teams_id_fk", + "tableFrom": "abuse_flags", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "abuse_flags_sequence_email_id_sequence_emails_id_fk": { + "name": "abuse_flags_sequence_email_id_sequence_emails_id_fk", + "tableFrom": "abuse_flags", + "tableTo": "sequence_emails", + "columnsFrom": [ + "sequence_email_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "abuse_flags_flag_id_unique": { + "name": "abuse_flags_flag_id_unique", + "nullsNotDistinct": false, + "columns": [ + "flag_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "abuse_flags_flag_id_check": { + "name": "abuse_flags_flag_id_check", + "value": "\"abuse_flags\".\"flag_id\" ~ '^abf_'" + }, + "abuse_flags_kind_check": { + "name": "abuse_flags_kind_check", + "value": "\"abuse_flags\".\"kind\" IN ('signup', 'content', 'import')" + }, + "abuse_flags_severity_check": { + "name": "abuse_flags_severity_check", + "value": "\"abuse_flags\".\"severity\" IN ('low', 'medium', 'high')" + }, + "abuse_flags_status_check": { + "name": "abuse_flags_status_check", + "value": "\"abuse_flags\".\"status\" IN ('open', 'released', 'confirmed')" + } + }, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_account_user_id_idx": { + "name": "auth_account_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_account_issuer_account_id_uidx": { + "name": "auth_account_issuer_account_id_uidx", + "columns": [ + { + "expression": "issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contact_custom_field_values": { + "name": "contact_custom_field_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value_type": { + "name": "value_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value_text": { + "name": "value_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "value_number": { + "name": "value_number", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "value_boolean": { + "name": "value_boolean", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "value_date": { + "name": "value_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "contact_custom_field_values_contact_key_idx": { + "name": "contact_custom_field_values_contact_key_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "contact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contact_custom_field_values_text_lookup_idx": { + "name": "contact_custom_field_values_text_lookup_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_text", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contact_custom_field_values_number_lookup_idx": { + "name": "contact_custom_field_values_number_lookup_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contact_custom_field_values_boolean_lookup_idx": { + "name": "contact_custom_field_values_boolean_lookup_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_boolean", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "contact_custom_field_values_date_lookup_idx": { + "name": "contact_custom_field_values_date_lookup_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contact_custom_field_values_team_id_teams_id_fk": { + "name": "contact_custom_field_values_team_id_teams_id_fk", + "tableFrom": "contact_custom_field_values", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "contact_custom_field_values_contact_id_contacts_id_fk": { + "name": "contact_custom_field_values_contact_id_contacts_id_fk", + "tableFrom": "contact_custom_field_values", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contacts": { + "name": "contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscribed": { + "name": "subscribed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "custom_fields": { + "name": "custom_fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "unsubscribe_token": { + "name": "unsubscribe_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "contacts_team_id_email_idx": { + "name": "contacts_team_id_email_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contacts_team_id_teams_id_fk": { + "name": "contacts_team_id_teams_id_fk", + "tableFrom": "contacts", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "contacts_contact_id_unique": { + "name": "contacts_contact_id_unique", + "nullsNotDistinct": false, + "columns": [ + "contact_id" + ] + }, + "contacts_unsubscribe_token_unique": { + "name": "contacts_unsubscribe_token_unique", + "nullsNotDistinct": false, + "columns": [ + "unsubscribe_token" + ] + } + }, + "policies": {}, + "checkConstraints": { + "contacts_contact_id_check": { + "name": "contacts_contact_id_check", + "value": "\"contacts\".\"contact_id\" ~ '^cnt_'" + } + }, + "isRLSEnabled": false + }, + "public.email_content_versions": { + "name": "email_content_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "version_id": { + "name": "version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence_email_id": { + "name": "sequence_email_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "email_content_versions_email_hash_idx": { + "name": "email_content_versions_email_hash_idx", + "columns": [ + { + "expression": "sequence_email_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "content_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_content_versions_team_id_teams_id_fk": { + "name": "email_content_versions_team_id_teams_id_fk", + "tableFrom": "email_content_versions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_content_versions_sequence_email_id_sequence_emails_id_fk": { + "name": "email_content_versions_sequence_email_id_sequence_emails_id_fk", + "tableFrom": "email_content_versions", + "tableTo": "sequence_emails", + "columnsFrom": [ + "sequence_email_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_content_versions_version_id_unique": { + "name": "email_content_versions_version_id_unique", + "nullsNotDistinct": false, + "columns": [ + "version_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "email_content_versions_version_id_check": { + "name": "email_content_versions_version_id_check", + "value": "\"email_content_versions\".\"version_id\" ~ '^ver_'" + } + }, + "isRLSEnabled": false + }, + "public.email_deliveries": { + "name": "email_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email_id": { + "name": "email_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "content_version_id": { + "name": "content_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_deliveries_team_id_teams_id_fk": { + "name": "email_deliveries_team_id_teams_id_fk", + "tableFrom": "email_deliveries", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_deliveries_sequence_id_sequences_id_fk": { + "name": "email_deliveries_sequence_id_sequences_id_fk", + "tableFrom": "email_deliveries", + "tableTo": "sequences", + "columnsFrom": [ + "sequence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_deliveries_contact_id_contacts_id_fk": { + "name": "email_deliveries_contact_id_contacts_id_fk", + "tableFrom": "email_deliveries", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_deliveries_email_id_sequence_emails_id_fk": { + "name": "email_deliveries_email_id_sequence_emails_id_fk", + "tableFrom": "email_deliveries", + "tableTo": "sequence_emails", + "columnsFrom": [ + "email_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_deliveries_content_version_id_email_content_versions_id_fk": { + "name": "email_deliveries_content_version_id_email_content_versions_id_fk", + "tableFrom": "email_deliveries", + "tableTo": "email_content_versions", + "columnsFrom": [ + "content_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_delivery_events": { + "name": "email_delivery_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "receipt_id": { + "name": "receipt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "outbound_message_id": { + "name": "outbound_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_key": { + "name": "provider_event_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "normalized_recipient": { + "name": "normalized_recipient", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bounce_class": { + "name": "bounce_class", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "smtp_code": { + "name": "smtp_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "enhanced_status_code": { + "name": "enhanced_status_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "remote_mta": { + "name": "remote_mta", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "email_delivery_events_connection_id_provider_event_key_idx": { + "name": "email_delivery_events_connection_id_provider_event_key_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_delivery_events_team_id_occurred_at_idx": { + "name": "email_delivery_events_team_id_occurred_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_delivery_events_outbound_message_id_idx": { + "name": "email_delivery_events_outbound_message_id_idx", + "columns": [ + { + "expression": "outbound_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_delivery_events_receipt_id_esp_webhook_receipts_id_fk": { + "name": "email_delivery_events_receipt_id_esp_webhook_receipts_id_fk", + "tableFrom": "email_delivery_events", + "tableTo": "esp_webhook_receipts", + "columnsFrom": [ + "receipt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_delivery_events_connection_id_esp_feedback_connections_id_fk": { + "name": "email_delivery_events_connection_id_esp_feedback_connections_id_fk", + "tableFrom": "email_delivery_events", + "tableTo": "esp_feedback_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_delivery_events_team_id_teams_id_fk": { + "name": "email_delivery_events_team_id_teams_id_fk", + "tableFrom": "email_delivery_events", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_delivery_events_outbound_message_id_outbound_messages_id_fk": { + "name": "email_delivery_events_outbound_message_id_outbound_messages_id_fk", + "tableFrom": "email_delivery_events", + "tableTo": "outbound_messages", + "columnsFrom": [ + "outbound_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_delivery_events_event_id_unique": { + "name": "email_delivery_events_event_id_unique", + "nullsNotDistinct": false, + "columns": [ + "event_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "email_delivery_events_event_id_check": { + "name": "email_delivery_events_event_id_check", + "value": "\"email_delivery_events\".\"event_id\" ~ '^evt_'" + } + }, + "isRLSEnabled": false + }, + "public.email_events": { + "name": "email_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email_id": { + "name": "email_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "link_index": { + "name": "link_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "bounce_type": { + "name": "bounce_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bounce_reason": { + "name": "bounce_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_events_team_id_teams_id_fk": { + "name": "email_events_team_id_teams_id_fk", + "tableFrom": "email_events", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_events_sequence_id_sequences_id_fk": { + "name": "email_events_sequence_id_sequences_id_fk", + "tableFrom": "email_events", + "tableTo": "sequences", + "columnsFrom": [ + "sequence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_events_contact_id_contacts_id_fk": { + "name": "email_events_contact_id_contacts_id_fk", + "tableFrom": "email_events", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_events_email_id_sequence_emails_id_fk": { + "name": "email_events_email_id_sequence_emails_id_fk", + "tableFrom": "email_events", + "tableTo": "sequence_emails", + "columnsFrom": [ + "email_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_suppression_actions": { + "name": "email_suppression_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "suppression_id": { + "name": "suppression_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_event_id": { + "name": "source_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_suppression_actions_suppression_id_created_at_idx": { + "name": "email_suppression_actions_suppression_id_created_at_idx", + "columns": [ + { + "expression": "suppression_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_suppression_actions_team_id_teams_id_fk": { + "name": "email_suppression_actions_team_id_teams_id_fk", + "tableFrom": "email_suppression_actions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_suppression_actions_suppression_id_email_suppressions_id_fk": { + "name": "email_suppression_actions_suppression_id_email_suppressions_id_fk", + "tableFrom": "email_suppression_actions", + "tableTo": "email_suppressions", + "columnsFrom": [ + "suppression_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_suppression_actions_source_event_id_email_delivery_events_id_fk": { + "name": "email_suppression_actions_source_event_id_email_delivery_events_id_fk", + "tableFrom": "email_suppression_actions", + "tableTo": "email_delivery_events", + "columnsFrom": [ + "source_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_suppression_actions_actor_user_id_user_id_fk": { + "name": "email_suppression_actions_actor_user_id_user_id_fk", + "tableFrom": "email_suppression_actions", + "tableTo": "user", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_suppressions": { + "name": "email_suppressions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "suppression_id": { + "name": "suppression_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "normalized_recipient": { + "name": "normalized_recipient", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipient_hash": { + "name": "recipient_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash_key_version": { + "name": "hash_key_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_event_id": { + "name": "source_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "first_suppressed_at": { + "name": "first_suppressed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_suppressed_at": { + "name": "last_suppressed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_by": { + "name": "released_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_reason": { + "name": "release_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "email_suppressions_team_id_recipient_hash_idx": { + "name": "email_suppressions_team_id_recipient_hash_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recipient_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_suppressions_team_id_active_idx": { + "name": "email_suppressions_team_id_active_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_suppressions_team_id_teams_id_fk": { + "name": "email_suppressions_team_id_teams_id_fk", + "tableFrom": "email_suppressions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_suppressions_source_event_id_email_delivery_events_id_fk": { + "name": "email_suppressions_source_event_id_email_delivery_events_id_fk", + "tableFrom": "email_suppressions", + "tableTo": "email_delivery_events", + "columnsFrom": [ + "source_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_suppressions_released_by_user_id_fk": { + "name": "email_suppressions_released_by_user_id_fk", + "tableFrom": "email_suppressions", + "tableTo": "user", + "columnsFrom": [ + "released_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_suppressions_suppression_id_unique": { + "name": "email_suppressions_suppression_id_unique", + "nullsNotDistinct": false, + "columns": [ + "suppression_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "email_suppressions_suppression_id_check": { + "name": "email_suppressions_suppression_id_check", + "value": "\"email_suppressions\".\"suppression_id\" ~ '^sup_'" + } + }, + "isRLSEnabled": false + }, + "public.email_templates": { + "name": "email_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'marketing'" + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "email_templates_team_id_title_idx": { + "name": "email_templates_team_id_title_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_templates_team_id_teams_id_fk": { + "name": "email_templates_team_id_teams_id_fk", + "tableFrom": "email_templates", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_templates_template_id_unique": { + "name": "email_templates_template_id_unique", + "nullsNotDistinct": false, + "columns": [ + "template_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "email_templates_template_id_check": { + "name": "email_templates_template_id_check", + "value": "\"email_templates\".\"template_id\" ~ '^tpl_'" + }, + "email_templates_purpose_check": { + "name": "email_templates_purpose_check", + "value": "\"email_templates\".\"purpose\" in ('marketing', 'transactional')" + } + }, + "isRLSEnabled": false + }, + "public.esp_config_team_grants": { + "name": "esp_config_team_grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "esp_config_id": { + "name": "esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "drain_until": { + "name": "drain_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "daily_limit": { + "name": "daily_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "monthly_limit": { + "name": "monthly_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_by_type": { + "name": "created_by_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "esp_config_team_grants_non_revoked_team_idx": { + "name": "esp_config_team_grants_non_revoked_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"esp_config_team_grants\".\"status\" <> 'revoked'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "esp_config_team_grants_team_organization_fk": { + "name": "esp_config_team_grants_team_organization_fk", + "tableFrom": "esp_config_team_grants", + "tableTo": "teams", + "columnsFrom": [ + "team_id", + "organization_id" + ], + "columnsTo": [ + "id", + "organization_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "esp_config_team_grants_esp_organization_fk": { + "name": "esp_config_team_grants_esp_organization_fk", + "tableFrom": "esp_config_team_grants", + "tableTo": "esp_configs", + "columnsFrom": [ + "esp_config_id", + "organization_id" + ], + "columnsTo": [ + "id", + "organization_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "esp_config_team_grants_grant_id_unique": { + "name": "esp_config_team_grants_grant_id_unique", + "nullsNotDistinct": false, + "columns": [ + "grant_id" + ] + }, + "esp_config_team_grants_id_organization_id_unique": { + "name": "esp_config_team_grants_id_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "organization_id" + ] + }, + "esp_config_team_grants_id_team_esp_unique": { + "name": "esp_config_team_grants_id_team_esp_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "team_id", + "esp_config_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "esp_config_team_grants_public_id_check": { + "name": "esp_config_team_grants_public_id_check", + "value": "\"esp_config_team_grants\".\"grant_id\" ~ '^egr_'" + }, + "esp_config_team_grants_status_check": { + "name": "esp_config_team_grants_status_check", + "value": "\"esp_config_team_grants\".\"status\" IN ('active', 'draining', 'suspended', 'revoked')" + }, + "esp_config_team_grants_limit_check": { + "name": "esp_config_team_grants_limit_check", + "value": "(\"esp_config_team_grants\".\"daily_limit\" IS NULL OR \"esp_config_team_grants\".\"daily_limit\" >= 0)\n AND (\"esp_config_team_grants\".\"monthly_limit\" IS NULL OR \"esp_config_team_grants\".\"monthly_limit\" >= 0)" + }, + "esp_config_team_grants_created_by_type_check": { + "name": "esp_config_team_grants_created_by_type_check", + "value": "\"esp_config_team_grants\".\"created_by_type\" IN ('user', 'organization_key', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.esp_configs": { + "name": "esp_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "esp_id": { + "name": "esp_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'smtp'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 587 + }, + "secure": { + "name": "secure", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_secret": { + "name": "encrypted_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_email": { + "name": "from_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_send_rate": { + "name": "max_send_rate", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "daily_send_limit": { + "name": "daily_send_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "warmup_target_daily": { + "name": "warmup_target_daily", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "warmup_started_at": { + "name": "warmup_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dkim_selector": { + "name": "dkim_selector", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "secret_version": { + "name": "secret_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "last_tested_at": { + "name": "last_tested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_status": { + "name": "last_test_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_test_error": { + "name": "last_test_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "drain_until": { + "name": "drain_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "esp_configs_organization_id_idx": { + "name": "esp_configs_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esp_configs_team_id_idx": { + "name": "esp_configs_team_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "esp_configs_organization_id_organizations_id_fk": { + "name": "esp_configs_organization_id_organizations_id_fk", + "tableFrom": "esp_configs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "esp_configs_team_id_teams_id_fk": { + "name": "esp_configs_team_id_teams_id_fk", + "tableFrom": "esp_configs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "esp_configs_esp_id_unique": { + "name": "esp_configs_esp_id_unique", + "nullsNotDistinct": false, + "columns": [ + "esp_id" + ] + }, + "esp_configs_id_organization_id_unique": { + "name": "esp_configs_id_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "organization_id" + ] + }, + "esp_configs_id_team_id_unique": { + "name": "esp_configs_id_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "esp_configs_esp_id_check": { + "name": "esp_configs_esp_id_check", + "value": "\"esp_configs\".\"esp_id\" ~ '^esp_'" + }, + "esp_configs_owner_check": { + "name": "esp_configs_owner_check", + "value": "(\"esp_configs\".\"owner_scope\" = 'organization' AND \"esp_configs\".\"organization_id\" IS NOT NULL AND \"esp_configs\".\"team_id\" IS NULL)\n OR (\"esp_configs\".\"owner_scope\" = 'team' AND \"esp_configs\".\"organization_id\" IS NULL AND \"esp_configs\".\"team_id\" IS NOT NULL)" + }, + "esp_configs_status_check": { + "name": "esp_configs_status_check", + "value": "\"esp_configs\".\"status\" IN ('draft', 'active', 'suspended', 'draining', 'retired')" + } + }, + "isRLSEnabled": false + }, + "public.esp_feedback_connections": { + "name": "esp_feedback_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_config_id": { + "name": "esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_credentials": { + "name": "encrypted_credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_encrypted_credentials": { + "name": "previous_encrypted_credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_credential_expires_at": { + "name": "previous_credential_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expected_topic_arn": { + "name": "expected_topic_arn", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_confirmed_at": { + "name": "subscription_confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "last_received_at": { + "name": "last_received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "esp_feedback_connections_team_id_idx": { + "name": "esp_feedback_connections_team_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esp_feedback_connections_esp_config_active_idx": { + "name": "esp_feedback_connections_esp_config_active_idx", + "columns": [ + { + "expression": "esp_config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"esp_feedback_connections\".\"esp_config_id\" is not null and \"esp_feedback_connections\".\"status\" not in ('retiring', 'disabled')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "esp_feedback_connections_organization_id_organizations_id_fk": { + "name": "esp_feedback_connections_organization_id_organizations_id_fk", + "tableFrom": "esp_feedback_connections", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "esp_feedback_connections_team_id_teams_id_fk": { + "name": "esp_feedback_connections_team_id_teams_id_fk", + "tableFrom": "esp_feedback_connections", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "esp_feedback_connections_esp_config_id_esp_configs_id_fk": { + "name": "esp_feedback_connections_esp_config_id_esp_configs_id_fk", + "tableFrom": "esp_feedback_connections", + "tableTo": "esp_configs", + "columnsFrom": [ + "esp_config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "esp_feedback_connections_connection_id_unique": { + "name": "esp_feedback_connections_connection_id_unique", + "nullsNotDistinct": false, + "columns": [ + "connection_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "esp_feedback_connections_connection_id_check": { + "name": "esp_feedback_connections_connection_id_check", + "value": "\"esp_feedback_connections\".\"connection_id\" ~ '^whc_'" + }, + "esp_feedback_connections_owner_check": { + "name": "esp_feedback_connections_owner_check", + "value": "(\n \"esp_feedback_connections\".\"owner_scope\" = 'organization'\n AND \"esp_feedback_connections\".\"organization_id\" IS NOT NULL\n AND \"esp_feedback_connections\".\"team_id\" IS NULL\n ) OR (\n \"esp_feedback_connections\".\"owner_scope\" = 'team'\n AND \"esp_feedback_connections\".\"organization_id\" IS NULL\n AND \"esp_feedback_connections\".\"team_id\" IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.esp_webhook_receipts": { + "name": "esp_webhook_receipts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "receipt_id": { + "name": "receipt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_request_id": { + "name": "provider_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_sha256": { + "name": "body_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_payload": { + "name": "encrypted_payload", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "safe_headers": { + "name": "safe_headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "esp_webhook_receipts_status_next_attempt_idx": { + "name": "esp_webhook_receipts_status_next_attempt_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esp_webhook_receipts_connection_id_provider_request_id_idx": { + "name": "esp_webhook_receipts_connection_id_provider_request_id_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "esp_webhook_receipts_connection_id_esp_feedback_connections_id_fk": { + "name": "esp_webhook_receipts_connection_id_esp_feedback_connections_id_fk", + "tableFrom": "esp_webhook_receipts", + "tableTo": "esp_feedback_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "esp_webhook_receipts_team_id_teams_id_fk": { + "name": "esp_webhook_receipts_team_id_teams_id_fk", + "tableFrom": "esp_webhook_receipts", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "esp_webhook_receipts_receipt_id_unique": { + "name": "esp_webhook_receipts_receipt_id_unique", + "nullsNotDistinct": false, + "columns": [ + "receipt_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "esp_webhook_receipts_receipt_id_check": { + "name": "esp_webhook_receipts_receipt_id_check", + "value": "\"esp_webhook_receipts\".\"receipt_id\" ~ '^whr_'" + } + }, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mail_dispatch_outbox": { + "name": "mail_dispatch_outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "dispatch_id": { + "name": "dispatch_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outbound_message_id": { + "name": "outbound_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_name": { + "name": "queue_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "job_name": { + "name": "job_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "publish_attempts": { + "name": "publish_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mail_dispatch_outbox_due_idx": { + "name": "mail_dispatch_outbox_due_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mail_dispatch_outbox_outbound_message_id_outbound_messages_id_fk": { + "name": "mail_dispatch_outbox_outbound_message_id_outbound_messages_id_fk", + "tableFrom": "mail_dispatch_outbox", + "tableTo": "outbound_messages", + "columnsFrom": [ + "outbound_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mail_dispatch_outbox_dispatch_id_unique": { + "name": "mail_dispatch_outbox_dispatch_id_unique", + "nullsNotDistinct": false, + "columns": [ + "dispatch_id" + ] + }, + "mail_dispatch_outbox_outbound_message_id_unique": { + "name": "mail_dispatch_outbox_outbound_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "outbound_message_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "mail_dispatch_outbox_dispatch_id_check": { + "name": "mail_dispatch_outbox_dispatch_id_check", + "value": "\"mail_dispatch_outbox\".\"dispatch_id\" ~ '^mdj_'" + }, + "mail_dispatch_outbox_state_check": { + "name": "mail_dispatch_outbox_state_check", + "value": "\"mail_dispatch_outbox\".\"state\" IN ('pending', 'publishing', 'published', 'cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_lit_id": { + "name": "media_lit_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "media_team_id_media_lit_id_idx": { + "name": "media_team_id_media_lit_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "media_lit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_team_id_created_at_idx": { + "name": "media_team_id_created_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_team_id_teams_id_fk": { + "name": "media_team_id_teams_id_fk", + "tableFrom": "media", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "nullsNotDistinct": false, + "columns": [ + "media_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "media_media_id_check": { + "name": "media_media_id_check", + "value": "\"media\".\"media_id\" ~ '^med_'" + } + }, + "isRLSEnabled": false + }, + "public.media_references": { + "name": "media_references", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_internal_id": { + "name": "resource_internal_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_public_id": { + "name": "resource_public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_resource_internal_id": { + "name": "parent_resource_internal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_resource_public_id": { + "name": "parent_resource_public_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "media_references_resource_idx": { + "name": "media_references_resource_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_internal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_references_media_id_idx": { + "name": "media_references_media_id_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_references_resource_media_idx": { + "name": "media_references_resource_media_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_internal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_references_team_id_teams_id_fk": { + "name": "media_references_team_id_teams_id_fk", + "tableFrom": "media_references", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "media_references_media_id_media_id_fk": { + "name": "media_references_media_id_media_id_fk", + "tableFrom": "media_references", + "tableTo": "media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_access_token_client_id_idx": { + "name": "auth_oauth_access_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_session_id_idx": { + "name": "auth_oauth_access_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_user_id_idx": { + "name": "auth_oauth_access_token_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_authorization_code_id_idx": { + "name": "auth_oauth_access_token_authorization_code_id_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_refresh_id_idx": { + "name": "auth_oauth_access_token_refresh_id_idx", + "columns": [ + { + "expression": "refresh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_client_user_id_idx": { + "name": "auth_oauth_client_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": [ + "client_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_client_resource_client_id_idx": { + "name": "auth_oauth_client_resource_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_client_resource_resource_id_idx": { + "name": "auth_oauth_client_resource_resource_id_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_client_resource_client_id_resource_id_idx": { + "name": "auth_oauth_client_resource_client_id_resource_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_resource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_oauth_consent_client_id_idx": { + "name": "auth_oauth_consent_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_consent_user_id_idx": { + "name": "auth_oauth_consent_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_post_login_team_selections": { + "name": "oauth_post_login_team_selections", + "schema": "", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_post_login_team_selections_session_id_session_id_fk": { + "name": "oauth_post_login_team_selections_session_id_session_id_fk", + "tableFrom": "oauth_post_login_team_selections", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_post_login_team_selections_team_id_teams_id_fk": { + "name": "oauth_post_login_team_selections_team_id_teams_id_fk", + "tableFrom": "oauth_post_login_team_selections", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_oauth_refresh_token_client_id_idx": { + "name": "auth_oauth_refresh_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_authorization_code_id_idx": { + "name": "auth_oauth_refresh_token_authorization_code_id_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_session_id_idx": { + "name": "auth_oauth_refresh_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_user_id_idx": { + "name": "auth_oauth_refresh_token_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "nullsNotDistinct": false, + "columns": [ + "identifier" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ongoing_sequences": { + "name": "ongoing_sequences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "next_email_scheduled_time": { + "name": "next_email_scheduled_time", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "priority": { + "name": "priority", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sent_email_ids": { + "name": "sent_email_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "ongoing_sequences_sequence_id_contact_id_idx": { + "name": "ongoing_sequences_sequence_id_contact_id_idx", + "columns": [ + { + "expression": "sequence_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "contact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ongoing_sequences_next_email_scheduled_time_idx": { + "name": "ongoing_sequences_next_email_scheduled_time_idx", + "columns": [ + { + "expression": "next_email_scheduled_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ongoing_sequences_team_id_teams_id_fk": { + "name": "ongoing_sequences_team_id_teams_id_fk", + "tableFrom": "ongoing_sequences", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ongoing_sequences_sequence_id_sequences_id_fk": { + "name": "ongoing_sequences_sequence_id_sequences_id_fk", + "tableFrom": "ongoing_sequences", + "tableTo": "sequences", + "columnsFrom": [ + "sequence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ongoing_sequences_contact_id_contacts_id_fk": { + "name": "ongoing_sequences_contact_id_contacts_id_fk", + "tableFrom": "ongoing_sequences", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_api_keys": { + "name": "organization_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_api_key_id": { + "name": "organization_api_key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_api_keys_organization_id_idx": { + "name": "organization_api_keys_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_api_keys_organization_id_organizations_id_fk": { + "name": "organization_api_keys_organization_id_organizations_id_fk", + "tableFrom": "organization_api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_api_keys_created_by_user_id_user_id_fk": { + "name": "organization_api_keys_created_by_user_id_user_id_fk", + "tableFrom": "organization_api_keys", + "tableTo": "user", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_api_keys_organization_api_key_id_unique": { + "name": "organization_api_keys_organization_api_key_id_unique", + "nullsNotDistinct": false, + "columns": [ + "organization_api_key_id" + ] + }, + "organization_api_keys_key_hash_unique": { + "name": "organization_api_keys_key_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "key_hash" + ] + } + }, + "policies": {}, + "checkConstraints": { + "organization_api_keys_public_id_check": { + "name": "organization_api_keys_public_id_check", + "value": "\"organization_api_keys\".\"organization_api_key_id\" ~ '^oak_'" + } + }, + "isRLSEnabled": false + }, + "public.organization_audit_events": { + "name": "organization_audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_config_id": { + "name": "esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_grant_id": { + "name": "esp_grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_audit_events_organization_id_created_at_idx": { + "name": "organization_audit_events_organization_id_created_at_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_audit_events_team_id_created_at_idx": { + "name": "organization_audit_events_team_id_created_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_audit_events_organization_id_organizations_id_fk": { + "name": "organization_audit_events_organization_id_organizations_id_fk", + "tableFrom": "organization_audit_events", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_delivery_policies": { + "name": "organization_delivery_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "default_esp_config_id": { + "name": "default_esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auto_grant_default_esp": { + "name": "auto_grant_default_esp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_daily_limit": { + "name": "default_daily_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "default_monthly_limit": { + "name": "default_monthly_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "aggregate_daily_limit": { + "name": "aggregate_daily_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "aggregate_monthly_limit": { + "name": "aggregate_monthly_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "team_esp_enabled_by_default": { + "name": "team_esp_enabled_by_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "team_can_change_default": { + "name": "team_can_change_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organization_delivery_policies_organization_id_organizations_id_fk": { + "name": "organization_delivery_policies_organization_id_organizations_id_fk", + "tableFrom": "organization_delivery_policies", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_delivery_policies_default_esp_fk": { + "name": "organization_delivery_policies_default_esp_fk", + "tableFrom": "organization_delivery_policies", + "tableTo": "esp_configs", + "columnsFrom": [ + "default_esp_config_id", + "organization_id" + ], + "columnsTo": [ + "id", + "organization_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_delivery_policies_organization_id_unique": { + "name": "organization_delivery_policies_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "organization_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "organization_delivery_policies_limit_check": { + "name": "organization_delivery_policies_limit_check", + "value": "(\"organization_delivery_policies\".\"default_daily_limit\" IS NULL OR \"organization_delivery_policies\".\"default_daily_limit\" >= 0)\n AND (\"organization_delivery_policies\".\"default_monthly_limit\" IS NULL OR \"organization_delivery_policies\".\"default_monthly_limit\" >= 0)\n AND (\"organization_delivery_policies\".\"aggregate_daily_limit\" IS NULL OR \"organization_delivery_policies\".\"aggregate_daily_limit\" >= 0)\n AND (\"organization_delivery_policies\".\"aggregate_monthly_limit\" IS NULL OR \"organization_delivery_policies\".\"aggregate_monthly_limit\" >= 0)" + } + }, + "isRLSEnabled": false + }, + "public.organization_esp_quota_reservations": { + "name": "organization_esp_quota_reservations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "reservation_id": { + "name": "reservation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outbound_message_id": { + "name": "outbound_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "day_period_start": { + "name": "day_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "month_period_start": { + "name": "month_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reserved'" + }, + "release_reason": { + "name": "release_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "committed_at": { + "name": "committed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "organization_esp_quota_reservations_outbound_message_id_outbound_messages_id_fk": { + "name": "organization_esp_quota_reservations_outbound_message_id_outbound_messages_id_fk", + "tableFrom": "organization_esp_quota_reservations", + "tableTo": "outbound_messages", + "columnsFrom": [ + "outbound_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "organization_esp_quota_reservations_grant_id_esp_config_team_grants_id_fk": { + "name": "organization_esp_quota_reservations_grant_id_esp_config_team_grants_id_fk", + "tableFrom": "organization_esp_quota_reservations", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "organization_esp_quota_reservations_organization_id_organizations_id_fk": { + "name": "organization_esp_quota_reservations_organization_id_organizations_id_fk", + "tableFrom": "organization_esp_quota_reservations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "organization_esp_quota_reservations_grant_organization_fk": { + "name": "organization_esp_quota_reservations_grant_organization_fk", + "tableFrom": "organization_esp_quota_reservations", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "grant_id", + "organization_id" + ], + "columnsTo": [ + "id", + "organization_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_esp_quota_reservations_reservation_id_unique": { + "name": "organization_esp_quota_reservations_reservation_id_unique", + "nullsNotDistinct": false, + "columns": [ + "reservation_id" + ] + }, + "organization_esp_quota_reservations_outbound_message_id_unique": { + "name": "organization_esp_quota_reservations_outbound_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "outbound_message_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "organization_esp_quota_reservations_reservation_id_check": { + "name": "organization_esp_quota_reservations_reservation_id_check", + "value": "\"organization_esp_quota_reservations\".\"reservation_id\" ~ '^qrs_'" + }, + "organization_esp_quota_reservations_state_check": { + "name": "organization_esp_quota_reservations_state_check", + "value": "\"organization_esp_quota_reservations\".\"state\" IN ('reserved', 'committed', 'released')" + } + }, + "isRLSEnabled": false + }, + "public.organization_esp_usage_buckets": { + "name": "organization_esp_usage_buckets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "bucket_scope": { + "name": "bucket_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "period_type": { + "name": "period_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_start": { + "name": "period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "reserved_count": { + "name": "reserved_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "accepted_count": { + "name": "accepted_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_esp_usage_buckets_grant_period_idx": { + "name": "organization_esp_usage_buckets_grant_period_idx", + "columns": [ + { + "expression": "grant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_esp_usage_buckets\".\"grant_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_esp_usage_buckets_organization_period_idx": { + "name": "organization_esp_usage_buckets_organization_period_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_esp_usage_buckets\".\"bucket_scope\" = 'organization'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_esp_usage_buckets_organization_id_organizations_id_fk": { + "name": "organization_esp_usage_buckets_organization_id_organizations_id_fk", + "tableFrom": "organization_esp_usage_buckets", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "organization_esp_usage_buckets_grant_id_esp_config_team_grants_id_fk": { + "name": "organization_esp_usage_buckets_grant_id_esp_config_team_grants_id_fk", + "tableFrom": "organization_esp_usage_buckets", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "organization_esp_usage_buckets_grant_organization_fk": { + "name": "organization_esp_usage_buckets_grant_organization_fk", + "tableFrom": "organization_esp_usage_buckets", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "grant_id", + "organization_id" + ], + "columnsTo": [ + "id", + "organization_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_esp_usage_buckets_scope_check": { + "name": "organization_esp_usage_buckets_scope_check", + "value": "(\n \"organization_esp_usage_buckets\".\"bucket_scope\" = 'grant' AND \"organization_esp_usage_buckets\".\"grant_id\" IS NOT NULL\n ) OR (\n \"organization_esp_usage_buckets\".\"bucket_scope\" = 'organization' AND \"organization_esp_usage_buckets\".\"grant_id\" IS NULL\n )" + }, + "organization_esp_usage_buckets_period_check": { + "name": "organization_esp_usage_buckets_period_check", + "value": "\"organization_esp_usage_buckets\".\"period_type\" IN ('day', 'month')" + }, + "organization_esp_usage_buckets_count_check": { + "name": "organization_esp_usage_buckets_count_check", + "value": "\"organization_esp_usage_buckets\".\"reserved_count\" >= 0 AND \"organization_esp_usage_buckets\".\"accepted_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.organization_members": { + "name": "organization_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_members_organization_id_user_id_idx": { + "name": "organization_members_organization_id_user_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_user_id_fk": { + "name": "organization_members_user_id_user_id_fk", + "tableFrom": "organization_members", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_members_role_check": { + "name": "organization_members_role_check", + "value": "\"organization_members\".\"role\" IN ('owner', 'admin', 'member')" + } + }, + "isRLSEnabled": false + }, + "public.organization_plan_policies": { + "name": "organization_plan_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "teams_limit_override": { + "name": "teams_limit_override", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "contacts_limit_override": { + "name": "contacts_limit_override", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "marketing_ramp_stage": { + "name": "marketing_ramp_stage", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "marketing_ramp_clean_days": { + "name": "marketing_ramp_clean_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "marketing_ramp_evaluated_at": { + "name": "marketing_ramp_evaluated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "past_due_since": { + "name": "past_due_since", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "pacing_exempt": { + "name": "pacing_exempt", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "signup_risk_score": { + "name": "signup_risk_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organization_plan_policies_organization_id_organizations_id_fk": { + "name": "organization_plan_policies_organization_id_organizations_id_fk", + "tableFrom": "organization_plan_policies", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_plan_policies_organization_id_unique": { + "name": "organization_plan_policies_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "organization_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "organization_plan_policies_overrides_check": { + "name": "organization_plan_policies_overrides_check", + "value": "(\"organization_plan_policies\".\"teams_limit_override\" IS NULL OR \"organization_plan_policies\".\"teams_limit_override\" > 0)\n AND (\"organization_plan_policies\".\"contacts_limit_override\" IS NULL OR \"organization_plan_policies\".\"contacts_limit_override\" > 0)" + }, + "organization_plan_policies_ramp_check": { + "name": "organization_plan_policies_ramp_check", + "value": "\"organization_plan_policies\".\"marketing_ramp_stage\" BETWEEN 0 AND 3 AND \"organization_plan_policies\".\"marketing_ramp_clean_days\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "pending_team_name": { + "name": "pending_team_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_organization_id_unique": { + "name": "organizations_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "organization_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "organizations_organization_id_check": { + "name": "organizations_organization_id_check", + "value": "\"organizations\".\"organization_id\" ~ '^org_'" + }, + "organizations_status_check": { + "name": "organizations_status_check", + "value": "\"organizations\".\"status\" IN ('pending_payment', 'active', 'suspended', 'abandoned', 'closed')" + } + }, + "isRLSEnabled": false + }, + "public.outbound_messages": { + "name": "outbound_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivery_source_type": { + "name": "delivery_source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "esp_config_id": { + "name": "esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_grant_id": { + "name": "esp_grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "feedback_connection_id": { + "name": "feedback_connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "submission_key": { + "name": "submission_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "campaign_delivery_id": { + "name": "campaign_delivery_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "transactional_email_id": { + "name": "transactional_email_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_recipient": { + "name": "normalized_recipient", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rfc_message_id": { + "name": "rfc_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "delivery_status": { + "name": "delivery_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "feedback_status": { + "name": "feedback_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "bounced_at": { + "name": "bounced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "complained_at": { + "name": "complained_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_event_at": { + "name": "last_event_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "outbound_messages_team_id_created_at_idx": { + "name": "outbound_messages_team_id_created_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbound_messages_connection_provider_msg_idx": { + "name": "outbound_messages_connection_provider_msg_idx", + "columns": [ + { + "expression": "feedback_connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbound_messages_team_id_recipient_created_at_idx": { + "name": "outbound_messages_team_id_recipient_created_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "normalized_recipient", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "outbound_messages_team_id_teams_id_fk": { + "name": "outbound_messages_team_id_teams_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "outbound_messages_esp_config_id_esp_configs_id_fk": { + "name": "outbound_messages_esp_config_id_esp_configs_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "esp_configs", + "columnsFrom": [ + "esp_config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "outbound_messages_esp_grant_id_esp_config_team_grants_id_fk": { + "name": "outbound_messages_esp_grant_id_esp_config_team_grants_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "esp_grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "outbound_messages_feedback_connection_id_esp_feedback_connections_id_fk": { + "name": "outbound_messages_feedback_connection_id_esp_feedback_connections_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "esp_feedback_connections", + "columnsFrom": [ + "feedback_connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "outbound_messages_campaign_delivery_id_email_deliveries_id_fk": { + "name": "outbound_messages_campaign_delivery_id_email_deliveries_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "email_deliveries", + "columnsFrom": [ + "campaign_delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "outbound_messages_transactional_email_id_transactional_emails_id_fk": { + "name": "outbound_messages_transactional_email_id_transactional_emails_id_fk", + "tableFrom": "outbound_messages", + "tableTo": "transactional_emails", + "columnsFrom": [ + "transactional_email_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "outbound_messages_message_id_unique": { + "name": "outbound_messages_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "message_id" + ] + }, + "outbound_messages_submission_key_unique": { + "name": "outbound_messages_submission_key_unique", + "nullsNotDistinct": false, + "columns": [ + "submission_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "outbound_messages_message_id_check": { + "name": "outbound_messages_message_id_check", + "value": "\"outbound_messages\".\"message_id\" ~ '^msg_'" + }, + "outbound_messages_delivery_pin_check": { + "name": "outbound_messages_delivery_pin_check", + "value": "(\n \"outbound_messages\".\"delivery_source_type\" = 'team'\n AND \"outbound_messages\".\"esp_config_id\" IS NOT NULL\n AND \"outbound_messages\".\"esp_grant_id\" IS NULL\n ) OR (\n \"outbound_messages\".\"delivery_source_type\" = 'organization'\n AND \"outbound_messages\".\"esp_config_id\" IS NOT NULL\n AND \"outbound_messages\".\"esp_grant_id\" IS NOT NULL\n ) OR (\n \"outbound_messages\".\"delivery_source_type\" IN ('team', 'organization')\n AND \"outbound_messages\".\"esp_config_id\" IS NULL\n AND \"outbound_messages\".\"esp_grant_id\" IS NULL\n AND \"outbound_messages\".\"delivery_status\" <> 'queued'\n )" + } + }, + "isRLSEnabled": false + }, + "public.plan_send_reservations": { + "name": "plan_send_reservations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "outbound_message_id": { + "name": "outbound_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bucket_id": { + "name": "bucket_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reserved'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "committed_at": { + "name": "committed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "released_at": { + "name": "released_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plan_send_reservations_outbound_uidx": { + "name": "plan_send_reservations_outbound_uidx", + "columns": [ + { + "expression": "outbound_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "plan_send_reservations_expiry_idx": { + "name": "plan_send_reservations_expiry_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plan_send_reservations_organization_id_organizations_id_fk": { + "name": "plan_send_reservations_organization_id_organizations_id_fk", + "tableFrom": "plan_send_reservations", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "plan_send_reservations_bucket_id_plan_send_usage_buckets_id_fk": { + "name": "plan_send_reservations_bucket_id_plan_send_usage_buckets_id_fk", + "tableFrom": "plan_send_reservations", + "tableTo": "plan_send_usage_buckets", + "columnsFrom": [ + "bucket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "plan_send_reservations_amount_check": { + "name": "plan_send_reservations_amount_check", + "value": "\"plan_send_reservations\".\"amount\" > 0" + }, + "plan_send_reservations_state_check": { + "name": "plan_send_reservations_state_check", + "value": "\"plan_send_reservations\".\"state\" IN ('reserved', 'committed', 'released')" + } + }, + "isRLSEnabled": false + }, + "public.plan_send_usage_buckets": { + "name": "plan_send_usage_buckets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "bucket_month": { + "name": "bucket_month", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "committed": { + "name": "committed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reserved": { + "name": "reserved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "plan_send_usage_buckets_organization_month_uidx": { + "name": "plan_send_usage_buckets_organization_month_uidx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bucket_month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plan_send_usage_buckets_organization_id_organizations_id_fk": { + "name": "plan_send_usage_buckets_organization_id_organizations_id_fk", + "tableFrom": "plan_send_usage_buckets", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "plan_send_usage_buckets_count_check": { + "name": "plan_send_usage_buckets_count_check", + "value": "\"plan_send_usage_buckets\".\"committed\" >= 0 AND \"plan_send_usage_buckets\".\"reserved\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.reputation_connections": { + "name": "reputation_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_secret": { + "name": "encrypted_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_label": { + "name": "account_label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_sync_at": { + "name": "next_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "alert_state": { + "name": "alert_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "subject_state": { + "name": "subject_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reputation_connections_team_id_provider_idx": { + "name": "reputation_connections_team_id_provider_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reputation_connections_next_sync_at_idx": { + "name": "reputation_connections_next_sync_at_idx", + "columns": [ + { + "expression": "next_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reputation_connections_team_id_teams_id_fk": { + "name": "reputation_connections_team_id_teams_id_fk", + "tableFrom": "reputation_connections", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reputation_connections_provider_check": { + "name": "reputation_connections_provider_check", + "value": "\"reputation_connections\".\"provider\" IN ('google_postmaster', 'microsoft_snds')" + }, + "reputation_connections_status_check": { + "name": "reputation_connections_status_check", + "value": "\"reputation_connections\".\"status\" IN ('active', 'error')" + } + }, + "isRLSEnabled": false + }, + "public.reputation_daily_stats": { + "name": "reputation_daily_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reputation_daily_stats_connection_subject_day_idx": { + "name": "reputation_daily_stats_connection_subject_day_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "day", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reputation_daily_stats_connection_id_reputation_connections_id_fk": { + "name": "reputation_daily_stats_connection_id_reputation_connections_id_fk", + "tableFrom": "reputation_daily_stats", + "tableTo": "reputation_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rules": { + "name": "rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_date_in_millis": { + "name": "event_date_in_millis", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "event_data": { + "name": "event_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "rules_team_id_teams_id_fk": { + "name": "rules_team_id_teams_id_fk", + "tableFrom": "rules", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "rules_sequence_id_sequences_id_fk": { + "name": "rules_sequence_id_sequences_id_fk", + "tableFrom": "rules", + "tableTo": "sequences", + "columnsFrom": [ + "sequence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "rules_rule_id_unique": { + "name": "rules_rule_id_unique", + "nullsNotDistinct": false, + "columns": [ + "rule_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "rules_rule_id_check": { + "name": "rules_rule_id_check", + "value": "\"rules\".\"rule_id\" ~ '^rule_'" + } + }, + "isRLSEnabled": false + }, + "public.segments": { + "name": "segments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "segment_id": { + "name": "segment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "segments_team_id_name_idx": { + "name": "segments_team_id_name_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "segments_team_id_teams_id_fk": { + "name": "segments_team_id_teams_id_fk", + "tableFrom": "segments", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "segments_segment_id_unique": { + "name": "segments_segment_id_unique", + "nullsNotDistinct": false, + "columns": [ + "segment_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "segments_segment_id_check": { + "name": "segments_segment_id_check", + "value": "\"segments\".\"segment_id\" ~ '^seg_'" + } + }, + "isRLSEnabled": false + }, + "public.sending_domains": { + "name": "sending_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "domain_id": { + "name": "domain_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "challenge_token_hash": { + "name": "challenge_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_checked_at": { + "name": "last_checked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_check_at": { + "name": "next_check_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failed_check_count": { + "name": "failed_check_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_failed_at": { + "name": "first_failed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sending_domains_organization_domain_uidx": { + "name": "sending_domains_organization_domain_uidx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sending_domains_organization_id_organizations_id_fk": { + "name": "sending_domains_organization_id_organizations_id_fk", + "tableFrom": "sending_domains", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sending_domains_domain_id_unique": { + "name": "sending_domains_domain_id_unique", + "nullsNotDistinct": false, + "columns": [ + "domain_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "sending_domains_domain_id_check": { + "name": "sending_domains_domain_id_check", + "value": "\"sending_domains\".\"domain_id\" ~ '^domain_'" + }, + "sending_domains_status_check": { + "name": "sending_domains_status_check", + "value": "\"sending_domains\".\"status\" IN ('pending', 'verified', 'revoked', 'failed')" + }, + "sending_domains_failed_check_count_check": { + "name": "sending_domains_failed_check_count_check", + "value": "\"sending_domains\".\"failed_check_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.sequence_emails": { + "name": "sequence_emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email_id": { + "name": "email_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "delay_in_millis": { + "name": "delay_in_millis", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 86400000 + }, + "published": { + "name": "published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_data": { + "name": "action_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_test_sent_at": { + "name": "last_test_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "content_scan_verdict": { + "name": "content_scan_verdict", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_scan_hash": { + "name": "content_scan_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_scan_reasons": { + "name": "content_scan_reasons", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "content_scanned_at": { + "name": "content_scanned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "sequence_emails_sequence_id_email_id_idx": { + "name": "sequence_emails_sequence_id_email_id_idx", + "columns": [ + { + "expression": "sequence_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sequence_emails_sequence_id_sequences_id_fk": { + "name": "sequence_emails_sequence_id_sequences_id_fk", + "tableFrom": "sequence_emails", + "tableTo": "sequences", + "columnsFrom": [ + "sequence_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "sequence_emails_email_id_check": { + "name": "sequence_emails_email_id_check", + "value": "\"sequence_emails\".\"email_id\" ~ '^email_'" + } + }, + "isRLSEnabled": false + }, + "public.sequences": { + "name": "sequences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence_id": { + "name": "sequence_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "delivery_source_intent": { + "name": "delivery_source_intent", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "delivery_source_type": { + "name": "delivery_source_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outbox_id": { + "name": "outbox_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_grant_id": { + "name": "esp_grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_data": { + "name": "trigger_data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "exclude_filter": { + "name": "exclude_filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "emails_order": { + "name": "emails_order", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "entrants": { + "name": "entrants", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "web_published": { + "name": "web_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "web_slug": { + "name": "web_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_seo_title": { + "name": "web_seo_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_seo_description": { + "name": "web_seo_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_og_image": { + "name": "web_og_image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_noindex": { + "name": "web_noindex", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "web_canonical_url": { + "name": "web_canonical_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "sequences_team_id_web_slug_idx": { + "name": "sequences_team_id_web_slug_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "web_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"sequences\".\"web_slug\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sequences_team_id_teams_id_fk": { + "name": "sequences_team_id_teams_id_fk", + "tableFrom": "sequences", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "sequences_outbox_id_esp_configs_id_fk": { + "name": "sequences_outbox_id_esp_configs_id_fk", + "tableFrom": "sequences", + "tableTo": "esp_configs", + "columnsFrom": [ + "outbox_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "sequences_esp_grant_id_esp_config_team_grants_id_fk": { + "name": "sequences_esp_grant_id_esp_config_team_grants_id_fk", + "tableFrom": "sequences", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "esp_grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sequences_sequence_id_unique": { + "name": "sequences_sequence_id_unique", + "nullsNotDistinct": false, + "columns": [ + "sequence_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "sequences_sequence_id_check": { + "name": "sequences_sequence_id_check", + "value": "\"sequences\".\"sequence_id\" ~ '^seq_'" + }, + "sequences_delivery_pin_check": { + "name": "sequences_delivery_pin_check", + "value": "(\n \"sequences\".\"delivery_source_type\" IS NULL\n AND \"sequences\".\"outbox_id\" IS NULL\n AND \"sequences\".\"esp_grant_id\" IS NULL\n ) OR (\n \"sequences\".\"delivery_source_type\" = 'team'\n AND \"sequences\".\"outbox_id\" IS NOT NULL\n AND \"sequences\".\"esp_grant_id\" IS NULL\n ) OR (\n \"sequences\".\"delivery_source_type\" = 'organization'\n AND \"sequences\".\"outbox_id\" IS NOT NULL\n AND \"sequences\".\"esp_grant_id\" IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_session_user_id_idx": { + "name": "auth_session_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mailing_address": { + "name": "mailing_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_enabled": { + "name": "web_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "web_handle": { + "name": "web_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_title": { + "name": "web_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_description": { + "name": "web_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_signup_form_id": { + "name": "web_signup_form_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "settings_team_id_teams_id_fk": { + "name": "settings_team_id_teams_id_fk", + "tableFrom": "settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "settings_web_signup_form_id_signup_forms_id_fk": { + "name": "settings_web_signup_form_id_signup_forms_id_fk", + "tableFrom": "settings", + "tableTo": "signup_forms", + "columnsFrom": [ + "web_signup_form_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_team_id_unique": { + "name": "settings_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + }, + "settings_web_handle_unique": { + "name": "settings_web_handle_unique", + "nullsNotDistinct": false, + "columns": [ + "web_handle" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signup_confirmations": { + "name": "signup_confirmations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "form_id": { + "name": "form_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "signup_confirmations_form_id_email_idx": { + "name": "signup_confirmations_form_id_email_idx", + "columns": [ + { + "expression": "form_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "signup_confirmations_form_id_signup_forms_id_fk": { + "name": "signup_confirmations_form_id_signup_forms_id_fk", + "tableFrom": "signup_confirmations", + "tableTo": "signup_forms", + "columnsFrom": [ + "form_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signup_confirmations_token_unique": { + "name": "signup_confirmations_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signup_forms": { + "name": "signup_forms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "form_id": { + "name": "form_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "double_opt_in": { + "name": "double_opt_in", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "redirect_url": { + "name": "redirect_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "signup_forms_team_id_idx": { + "name": "signup_forms_team_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "signup_forms_team_id_teams_id_fk": { + "name": "signup_forms_team_id_teams_id_fk", + "tableFrom": "signup_forms", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signup_forms_form_id_unique": { + "name": "signup_forms_form_id_unique", + "nullsNotDistinct": false, + "columns": [ + "form_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "signup_forms_form_id_check": { + "name": "signup_forms_form_id_check", + "value": "\"signup_forms\".\"form_id\" ~ '^frm_'" + } + }, + "isRLSEnabled": false + }, + "public.team_api_keys": { + "name": "team_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_api_key_id": { + "name": "team_api_key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_type": { + "name": "created_by_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_api_keys_team_id_idx": { + "name": "team_api_keys_team_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_api_keys_team_id_teams_id_fk": { + "name": "team_api_keys_team_id_teams_id_fk", + "tableFrom": "team_api_keys", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_api_keys_team_api_key_id_unique": { + "name": "team_api_keys_team_api_key_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_api_key_id" + ] + }, + "team_api_keys_key_hash_unique": { + "name": "team_api_keys_key_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "key_hash" + ] + } + }, + "policies": {}, + "checkConstraints": { + "team_api_keys_public_id_check": { + "name": "team_api_keys_public_id_check", + "value": "\"team_api_keys\".\"team_api_key_id\" ~ '^tak_'" + }, + "team_api_keys_created_by_type_check": { + "name": "team_api_keys_created_by_type_check", + "value": "\"team_api_keys\".\"created_by_type\" IN ('user', 'organization_key', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.team_delivery_settings": { + "name": "team_delivery_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_esp_enabled": { + "name": "team_esp_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "team_can_change_default": { + "name": "team_can_change_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "default_source": { + "name": "default_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_team_esp_config_id": { + "name": "default_team_esp_config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_delivery_settings_team_id_teams_id_fk": { + "name": "team_delivery_settings_team_id_teams_id_fk", + "tableFrom": "team_delivery_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_delivery_settings_default_team_esp_fk": { + "name": "team_delivery_settings_default_team_esp_fk", + "tableFrom": "team_delivery_settings", + "tableTo": "esp_configs", + "columnsFrom": [ + "default_team_esp_config_id", + "team_id" + ], + "columnsTo": [ + "id", + "team_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_delivery_settings_team_id_unique": { + "name": "team_delivery_settings_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "team_delivery_settings_default_source_check": { + "name": "team_delivery_settings_default_source_check", + "value": "\"team_delivery_settings\".\"default_source\" IS NULL OR \"team_delivery_settings\".\"default_source\" IN ('organization', 'team')" + } + }, + "isRLSEnabled": false + }, + "public.team_members": { + "name": "team_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_members_team_id_user_id_idx": { + "name": "team_members_team_id_user_id_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_members_team_id_teams_id_fk": { + "name": "team_members_team_id_teams_id_fk", + "tableFrom": "team_members", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_members_user_id_user_id_fk": { + "name": "team_members_user_id_user_id_fk", + "tableFrom": "team_members", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "team_members_role_check": { + "name": "team_members_role_check", + "value": "\"team_members\".\"role\" IN ('admin', 'member')" + } + }, + "isRLSEnabled": false + }, + "public.team_sending_controls": { + "name": "team_sending_controls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "reason_code": { + "name": "reason_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'automatic'" + }, + "entered_at": { + "name": "entered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "evaluated_at": { + "name": "evaluated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "minimum_hold_until": { + "name": "minimum_hold_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "operator_user_id": { + "name": "operator_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "operator_reason": { + "name": "operator_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "overridden_at": { + "name": "overridden_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "clean_evaluation_days": { + "name": "clean_evaluation_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_sending_controls_team_id_teams_id_fk": { + "name": "team_sending_controls_team_id_teams_id_fk", + "tableFrom": "team_sending_controls", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "team_sending_controls_operator_user_id_user_id_fk": { + "name": "team_sending_controls_operator_user_id_user_id_fk", + "tableFrom": "team_sending_controls", + "tableTo": "user", + "columnsFrom": [ + "operator_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_sending_controls_team_id_unique": { + "name": "team_sending_controls_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "team_sending_controls_status_check": { + "name": "team_sending_controls_status_check", + "value": "\"team_sending_controls\".\"status\" IN ('normal', 'warned', 'marketing_paused', 'all_paused')" + }, + "team_sending_controls_source_check": { + "name": "team_sending_controls_source_check", + "value": "\"team_sending_controls\".\"source\" IN ('automatic', 'operator', 'abuse')" + }, + "team_sending_controls_clean_days_check": { + "name": "team_sending_controls_clean_days_check", + "value": "\"team_sending_controls\".\"clean_evaluation_days\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provisioning_request_hash": { + "name": "provisioning_request_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "teams_organization_id_external_id_idx": { + "name": "teams_organization_id_external_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"teams\".\"external_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "teams_organization_id_organizations_id_fk": { + "name": "teams_organization_id_organizations_id_fk", + "tableFrom": "teams", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "teams_team_id_unique": { + "name": "teams_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + }, + "teams_id_organization_id_unique": { + "name": "teams_id_organization_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "organization_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "teams_team_id_check": { + "name": "teams_team_id_check", + "value": "\"teams\".\"team_id\" ~ '^team_'" + }, + "teams_status_check": { + "name": "teams_status_check", + "value": "\"teams\".\"status\" IN ('active', 'sending_suspended', 'archived')" + } + }, + "isRLSEnabled": false + }, + "public.transactional_emails": { + "name": "transactional_emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "txe_id": { + "name": "txe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delivery_source_type": { + "name": "delivery_source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outbox_id": { + "name": "outbox_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "esp_grant_id": { + "name": "esp_grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "to_email": { + "name": "to_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_email": { + "name": "from_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "html": { + "name": "html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "track_opens": { + "name": "track_opens", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "track_clicks": { + "name": "track_clicks", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "open_count": { + "name": "open_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "click_count": { + "name": "click_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "transactional_emails_team_id_idempotency_key_idx": { + "name": "transactional_emails_team_id_idempotency_key_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"transactional_emails\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "transactional_emails_team_id_created_at_idx": { + "name": "transactional_emails_team_id_created_at_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "transactional_emails_team_id_status_idx": { + "name": "transactional_emails_team_id_status_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "transactional_emails_team_id_teams_id_fk": { + "name": "transactional_emails_team_id_teams_id_fk", + "tableFrom": "transactional_emails", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "transactional_emails_outbox_id_esp_configs_id_fk": { + "name": "transactional_emails_outbox_id_esp_configs_id_fk", + "tableFrom": "transactional_emails", + "tableTo": "esp_configs", + "columnsFrom": [ + "outbox_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "transactional_emails_esp_grant_id_esp_config_team_grants_id_fk": { + "name": "transactional_emails_esp_grant_id_esp_config_team_grants_id_fk", + "tableFrom": "transactional_emails", + "tableTo": "esp_config_team_grants", + "columnsFrom": [ + "esp_grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "transactional_emails_contact_id_contacts_id_fk": { + "name": "transactional_emails_contact_id_contacts_id_fk", + "tableFrom": "transactional_emails", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "transactional_emails_txe_id_unique": { + "name": "transactional_emails_txe_id_unique", + "nullsNotDistinct": false, + "columns": [ + "txe_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "transactional_emails_txe_id_check": { + "name": "transactional_emails_txe_id_check", + "value": "\"transactional_emails\".\"txe_id\" ~ '^txe_'" + }, + "transactional_emails_delivery_pin_check": { + "name": "transactional_emails_delivery_pin_check", + "value": "(\n \"transactional_emails\".\"delivery_source_type\" = 'team'\n AND \"transactional_emails\".\"outbox_id\" IS NOT NULL\n AND \"transactional_emails\".\"esp_grant_id\" IS NULL\n ) OR (\n \"transactional_emails\".\"delivery_source_type\" = 'organization'\n AND \"transactional_emails\".\"outbox_id\" IS NOT NULL\n AND \"transactional_emails\".\"esp_grant_id\" IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.trial_claims": { + "name": "trial_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_email_fingerprint": { + "name": "verified_email_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint_key_version": { + "name": "fingerprint_key_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trial_key": { + "name": "trial_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reserved'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "trial_claims_user_trial_uidx": { + "name": "trial_claims_user_trial_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trial_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"trial_claims\".\"status\" <> 'released'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "trial_claims_email_trial_uidx": { + "name": "trial_claims_email_trial_uidx", + "columns": [ + { + "expression": "verified_email_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trial_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"trial_claims\".\"status\" <> 'released'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "trial_claims_checkout_attempt_idx": { + "name": "trial_claims_checkout_attempt_idx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "trial_claims_user_id_user_id_fk": { + "name": "trial_claims_user_id_user_id_fk", + "tableFrom": "trial_claims", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "trial_claims_organization_id_organizations_id_fk": { + "name": "trial_claims_organization_id_organizations_id_fk", + "tableFrom": "trial_claims", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "trial_claims_status_check": { + "name": "trial_claims_status_check", + "value": "\"trial_claims\".\"status\" IN ('reserved', 'redeemed', 'released')" + } + }, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_organization_id": { + "name": "default_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_default_organization_id_organizations_id_fk": { + "name": "user_default_organization_id_organizations_id_fk", + "tableFrom": "user", + "tableTo": "organizations", + "columnsFrom": [ + "default_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revision_items": { + "name": "billing_catalog_revision_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "catalog_revision_id": { + "name": "catalog_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_catalog_revision_items_revision_key_uidx": { + "name": "billing_catalog_revision_items_revision_key_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_catalog_revision_items_revision_price_uidx": { + "name": "billing_catalog_revision_items_revision_price_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_price_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk": { + "name": "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_catalog_revisions", + "columnsFrom": [ + "catalog_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revisions": { + "name": "billing_catalog_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "checkout_provider": { + "name": "checkout_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_verification'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_catalog_revisions_active_provider_uidx": { + "name": "billing_catalog_revisions_active_provider_uidx", + "columns": [ + { + "expression": "checkout_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_catalog_revisions\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_catalog_revisions_revision_unique": { + "name": "billing_catalog_revisions_revision_unique", + "nullsNotDistinct": false, + "columns": [ + "revision" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_catalog_revisions_status_check": { + "name": "billing_catalog_revisions_status_check", + "value": "\"billing_catalog_revisions\".\"status\" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')" + }, + "billing_catalog_revisions_revision_check": { + "name": "billing_catalog_revisions_revision_check", + "value": "\"billing_catalog_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.billing_checkout_attempts": { + "name": "billing_checkout_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attempt_id": { + "name": "attempt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_plan": { + "name": "requested_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_interval": { + "name": "requested_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "quoted_amount_minor": { + "name": "quoted_amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quoted_currency": { + "name": "quoted_currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_checkout_session_id": { + "name": "provider_checkout_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_url_encrypted": { + "name": "checkout_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_checkout_attempts_provider_session_uidx": { + "name": "billing_checkout_attempts_provider_session_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_checkout_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"provider_checkout_session_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_idempotency_uidx": { + "name": "billing_checkout_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_entity_nonterminal_uidx": { + "name": "billing_checkout_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_checkout_attempts_billable_entity_id_organizations_id_fk": { + "name": "billing_checkout_attempts_billable_entity_id_organizations_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "organizations", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_payer_id_user_id_fk": { + "name": "billing_checkout_attempts_payer_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_checkout_attempts_attempt_id_unique": { + "name": "billing_checkout_attempts_attempt_id_unique", + "nullsNotDistinct": false, + "columns": [ + "attempt_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_checkout_attempts_status_check": { + "name": "billing_checkout_attempts_status_check", + "value": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')" + }, + "billing_checkout_attempts_amount_check": { + "name": "billing_checkout_attempts_amount_check", + "value": "\"billing_checkout_attempts\".\"quoted_amount_minor\" > 0" + }, + "billing_checkout_attempts_plan_check": { + "name": "billing_checkout_attempts_plan_check", + "value": "\"billing_checkout_attempts\".\"requested_plan\" IN ('starter', 'pro', 'business')" + }, + "billing_checkout_attempts_interval_check": { + "name": "billing_checkout_attempts_interval_check", + "value": "\"billing_checkout_attempts\".\"requested_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_change_attempts": { + "name": "billing_plan_change_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "change_id": { + "name": "change_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_catalog_revision": { + "name": "current_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_billing_price_entry_id": { + "name": "current_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_plan": { + "name": "current_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_interval": { + "name": "current_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_catalog_revision": { + "name": "target_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_billing_price_entry_id": { + "name": "target_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_plan": { + "name": "target_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_interval": { + "name": "target_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_offer_key": { + "name": "target_offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proration_mode": { + "name": "proration_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_payment_id": { + "name": "provider_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_url_encrypted": { + "name": "payment_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_plan_change_attempts_idempotency_uidx": { + "name": "billing_plan_change_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_plan_change_attempts_entity_nonterminal_uidx": { + "name": "billing_plan_change_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_plan_change_attempts_billable_entity_id_organizations_id_fk": { + "name": "billing_plan_change_attempts_billable_entity_id_organizations_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "organizations", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_payer_id_user_id_fk": { + "name": "billing_plan_change_attempts_payer_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "current_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "target_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_change_attempts_change_id_unique": { + "name": "billing_plan_change_attempts_change_id_unique", + "nullsNotDistinct": false, + "columns": [ + "change_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_plan_change_attempts_status_check": { + "name": "billing_plan_change_attempts_status_check", + "value": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')" + }, + "billing_plan_change_attempts_effective_at_check": { + "name": "billing_plan_change_attempts_effective_at_check", + "value": "\"billing_plan_change_attempts\".\"effective_at\" IN ('immediately', 'next_billing_date')" + }, + "billing_plan_change_attempts_proration_mode_check": { + "name": "billing_plan_change_attempts_proration_mode_check", + "value": "\"billing_plan_change_attempts\".\"proration_mode\" IN ('prorated_immediately', 'do_not_bill')" + }, + "billing_plan_change_attempts_current_plan_check": { + "name": "billing_plan_change_attempts_current_plan_check", + "value": "\"billing_plan_change_attempts\".\"current_plan\" IN ('starter', 'pro', 'business')" + }, + "billing_plan_change_attempts_target_plan_check": { + "name": "billing_plan_change_attempts_target_plan_check", + "value": "\"billing_plan_change_attempts\".\"target_plan\" IN ('starter', 'pro', 'business')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_states": { + "name": "billing_plan_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "active_subscription_id": { + "name": "active_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "projection_version": { + "name": "projection_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "billing_plan_states_billable_entity_id_organizations_id_fk": { + "name": "billing_plan_states_billable_entity_id_organizations_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "organizations", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "active_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_states_billable_entity_id_unique": { + "name": "billing_plan_states_billable_entity_id_unique", + "nullsNotDistinct": false, + "columns": [ + "billable_entity_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_price_entries": { + "name": "billing_price_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_minor": { + "name": "amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_trial_days": { + "name": "provider_trial_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_price_entries_provider_product_uidx": { + "name": "billing_price_entries_provider_product_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_price_entries_offer_key_idx": { + "name": "billing_price_entries_offer_key_idx", + "columns": [ + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_price_entries_amount_check": { + "name": "billing_price_entries_amount_check", + "value": "\"billing_price_entries\".\"amount_minor\" > 0" + }, + "billing_price_entries_trial_days_check": { + "name": "billing_price_entries_trial_days_check", + "value": "\"billing_price_entries\".\"provider_trial_days\" >= 0" + }, + "billing_price_entries_currency_check": { + "name": "billing_price_entries_currency_check", + "value": "\"billing_price_entries\".\"currency\" ~ '^[A-Z]{3}$'" + }, + "billing_price_entries_plan_check": { + "name": "billing_price_entries_plan_check", + "value": "\"billing_price_entries\".\"plan\" IN ('starter', 'pro', 'business')" + }, + "billing_price_entries_interval_check": { + "name": "billing_price_entries_interval_check", + "value": "\"billing_price_entries\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_provider_customers": { + "name": "billing_provider_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_provider_customers_provider_payer_uidx": { + "name": "billing_provider_customers_provider_payer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_provider_customer_uidx": { + "name": "billing_provider_customers_provider_customer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_provider_customers\".\"provider_customer_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_idempotency_uidx": { + "name": "billing_provider_customers_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_provider_customers_payer_id_user_id_fk": { + "name": "billing_provider_customers_payer_id_user_id_fk", + "tableFrom": "billing_provider_customers", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_provider_customers_status_check": { + "name": "billing_provider_customers_status_check", + "value": "\"billing_provider_customers\".\"status\" IN ('creating', 'active', 'conflicted')" + } + }, + "isRLSEnabled": false + }, + "public.billing_reconciliation_jobs": { + "name": "billing_reconciliation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plan_change_attempt_id": { + "name": "plan_change_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_reconciliation_jobs_live_checkout_uidx": { + "name": "billing_reconciliation_jobs_live_checkout_uidx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_plan_change_uidx": { + "name": "billing_reconciliation_jobs_live_plan_change_uidx", + "columns": [ + { + "expression": "plan_change_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_subscription_uidx": { + "name": "billing_reconciliation_jobs_live_subscription_uidx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_customer_uidx": { + "name": "billing_reconciliation_jobs_live_customer_uidx", + "columns": [ + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk": { + "name": "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_plan_change_attempts", + "columnsFrom": [ + "plan_change_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk": { + "name": "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "provider_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_reconciliation_jobs_exactly_one_subject": { + "name": "billing_reconciliation_jobs_exactly_one_subject", + "value": "((CASE WHEN \"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL THEN 1 ELSE 0 END)) = 1" + }, + "billing_reconciliation_jobs_status_check": { + "name": "billing_reconciliation_jobs_status_check", + "value": "\"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')" + }, + "billing_reconciliation_jobs_operation_check": { + "name": "billing_reconciliation_jobs_operation_check", + "value": "\"billing_reconciliation_jobs\".\"operation\" = 'reconcile' OR (\"billing_reconciliation_jobs\".\"operation\" = 'cancellation' AND \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_checkout_attempt_id": { + "name": "origin_checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "current_period_starts_at": { + "name": "current_period_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_ends_at": { + "name": "current_period_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paid_through_at": { + "name": "paid_through_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_entitlement_source": { + "name": "is_entitlement_source", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_occurred_at": { + "name": "provider_occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "provider_version": { + "name": "provider_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_at": { + "name": "last_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reconciled_at": { + "name": "last_reconciled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "billing_subscriptions_provider_subscription_uidx": { + "name": "billing_subscriptions_provider_subscription_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_entity_source_uidx": { + "name": "billing_subscriptions_entity_source_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_subscriptions\".\"is_entitlement_source\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_subscriptions_billable_entity_id_organizations_id_fk": { + "name": "billing_subscriptions_billable_entity_id_organizations_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "organizations", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_payer_id_user_id_fk": { + "name": "billing_subscriptions_payer_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "origin_checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_subscriptions_status_check": { + "name": "billing_subscriptions_status_check", + "value": "\"billing_subscriptions\".\"status\" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')" + }, + "billing_subscriptions_plan_check": { + "name": "billing_subscriptions_plan_check", + "value": "\"billing_subscriptions\".\"plan\" IN ('starter', 'pro', 'business')" + }, + "billing_subscriptions_interval_check": { + "name": "billing_subscriptions_interval_check", + "value": "\"billing_subscriptions\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_webhook_events": { + "name": "billing_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_encrypted": { + "name": "payload_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_key_version": { + "name": "payload_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_key_version": { + "name": "verified_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_webhook_events_provider_event_uidx": { + "name": "billing_webhook_events_provider_event_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_webhook_events_queue_idx": { + "name": "billing_webhook_events_queue_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_webhook_events_status_check": { + "name": "billing_webhook_events_status_check", + "value": "\"billing_webhook_events\".\"status\" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/_journal.json b/apps/api/drizzle/meta/_journal.json index ffff470..43a2ea8 100644 --- a/apps/api/drizzle/meta/_journal.json +++ b/apps/api/drizzle/meta/_journal.json @@ -162,6 +162,13 @@ "when": 1792603046941, "tag": "0022_esp_dkim_selector", "breakpoints": true + }, + { + "idx": 23, + "version": "7", + "when": 1792652093948, + "tag": "0023_sent_content_versions", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/api/src/automation/process-ongoing-sequence.test.ts b/apps/api/src/automation/process-ongoing-sequence.test.ts index 5dbe8a5..9ac43f9 100644 --- a/apps/api/src/automation/process-ongoing-sequence.test.ts +++ b/apps/api/src/automation/process-ongoing-sequence.test.ts @@ -27,7 +27,13 @@ const { sendMail } = await import("../mail/send"); const { SendDeferredError } = await import("../mail/rate-limit"); const { seedTeamAndContact, truncateAll } = await import("../test/db"); type TestDb = import("../test/db").TestDb; -const { seedOngoingSequence, seedSequence } = await import("../test/fixtures"); +const { emailContent, seedOngoingSequence, seedSequence } = await import( + "../test/fixtures" +); +const { emailContentVersions } = await import("../db/schema"); +const { default: webViewRoutes } = await import("../tracking/web-view"); +const { requestApp } = await import("../test/http"); +const express = (await import("express")).default; const { getUnsubLink } = await import("../utils/mail"); const { getNextPublishedEmail, processOngoingSequence } = await import( "./process-ongoing-sequence" @@ -302,6 +308,57 @@ describe("processOngoingSequence", () => { expect(mockedSendMail.mock.calls[0][0].replyTo).toBe("support@example.com"); }); + it("keeps each send's web version at what was sent when the email is edited mid-sequence", async () => { + const { team, contact } = await seedTeamAndContact(tdb); + const { sequenceRow, emailRows } = await seedSequence(tdb, { + teamId: team.id, + emails: [{ emailId: "email_e1" }], + }); + const viewPath = (html: string) => + new URL(html.match(/href="(https:\/\/sendlit\.test\/view\/[^"]+)"/)?.[1] ?? "") + .pathname; + const webView = express().use(webViewRoutes); + + const before = await seedOngoingSequence(tdb, { + teamId: team.id, + sequenceId: sequenceRow.id, + contactId: contact.id, + }); + await processOngoingSequence(before.id); + await tdb + .update(sequenceEmails) + .set({ content: emailContent({ text: "Edited for later contacts" }) }) + .where(eq(sequenceEmails.id, emailRows[0].id)); + const after = await seedOngoingSequence(tdb, { + teamId: team.id, + sequenceId: sequenceRow.id, + contactId: contact.id, + }); + await processOngoingSequence(after.id); + + const [earlier, later] = mockedSendMail.mock.calls.map(([mail]) => mail.html); + expect(earlier).toContain("Hello Ada Lovelace!"); + expect(later).toContain("Edited for later contacts"); + const earlierPage = await requestApp(webView, viewPath(earlier)); + expect(earlierPage.body).toContain("Hello Ada Lovelace!"); + expect(earlierPage.body).not.toContain("Edited for later contacts"); + const laterPage = await requestApp(webView, viewPath(later)); + expect(laterPage.body).toContain("Edited for later contacts"); + + const versions = await tdb + .select() + .from(emailContentVersions) + .where(eq(emailContentVersions.sequenceEmailId, emailRows[0].id)); + expect(versions).toHaveLength(2); + const deliveries = await tdb + .select() + .from(emailDeliveries) + .where(eq(emailDeliveries.emailId, emailRows[0].id)); + expect(new Set(deliveries.map((d) => d.contentVersionId))).toEqual( + new Set(versions.map((v) => v.id)), + ); + }); + it("renders merge tags, the tracking pixel, and click-tracked links", async () => { const { team, contact } = await seedTeamAndContact(tdb); const { sequenceRow } = await seedSequence(tdb, { diff --git a/apps/api/src/automation/process-ongoing-sequence.ts b/apps/api/src/automation/process-ongoing-sequence.ts index 1808821..25cb439 100644 --- a/apps/api/src/automation/process-ongoing-sequence.ts +++ b/apps/api/src/automation/process-ongoing-sequence.ts @@ -27,6 +27,7 @@ import { renderCampaignHtml } from "../mail/campaign"; import { quotaExhaustionDeferral, SendDeferredError } from "../mail/rate-limit"; import { sendMail } from "../mail/send"; import { captureError, captureEvent } from "../observability"; +import { getOrCreateContentVersion } from "../sequences/content-versions"; import logger from "../services/log"; import { getTeam } from "../team/queries"; import { normalizeEmail } from "../utils/email"; @@ -263,12 +264,18 @@ async function attemptMailSending({ return; } + // The "view in browser" link shows this copy, not later edits. + const contentVersion = await getOrCreateContentVersion({ + ...email, + teamId: team.id, + }); // NOTE: these ids must stay **public** — they get embedded into the // outgoing email's tracking pixel/links. `ongoingSequence.sequenceId` is // the *internal* id; use `sequence.sequenceId` (public) instead. const contentWithTrackedLinks = await renderCampaignHtml({ team, content: email.content, + contentVersionId: contentVersion.versionId, subscriber: { email: contact.email, name: contact.name, @@ -333,6 +340,7 @@ async function attemptMailSending({ sequenceId: sequence.id, contactId: contact.id, emailId: email.id, + contentVersionId: contentVersion.id, }) .returning(); await markOutboundAccepted(outbound.id, { diff --git a/apps/api/src/db/schema-core.ts b/apps/api/src/db/schema-core.ts index b0352c6..6ebd013 100644 --- a/apps/api/src/db/schema-core.ts +++ b/apps/api/src/db/schema-core.ts @@ -1428,6 +1428,44 @@ export const sequenceEmails = pgTable( }), ); +/** What a broadcast or sequence email looked like when it was sent: one + * row per distinct subject and content (`contentHash` from + * `abuse/content-scan.ts`), never updated. A send's "view in browser" link + * points at its row, so later edits don't change what earlier recipients + * see. Deleted with the email. */ +export const emailContentVersions = pgTable( + "email_content_versions", + { + id: uuid("id").$defaultFn(genId).primaryKey(), + /** Public id, carried in "view in browser" links. */ + versionId: text("version_id") + .notNull() + .unique() + .$defaultFn(() => genPublicId("ver")), + teamId: uuid("team_id") + .notNull() + .references(() => teams.id, { onDelete: "cascade" }), + sequenceEmailId: uuid("sequence_email_id") + .notNull() + .references(() => sequenceEmails.id, { onDelete: "cascade" }), + contentHash: text("content_hash").notNull(), + subject: text("subject").notNull(), + content: jsonb("content").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + }, + (table) => ({ + emailHashIdx: uniqueIndex("email_content_versions_email_hash_idx").on( + table.sequenceEmailId, + table.contentHash, + ), + versionIdCheck: publicIdCheck( + "email_content_versions_version_id_check", + table.versionId, + "ver", + ), + }), +); + /** A scheduled trigger for a sequence — e.g. "fire DATE_OCCURRED for broadcast X at * time T", processed by `automation/process-rules.ts`. Not exposed via any * REST/MCP route today. */ @@ -1511,6 +1549,11 @@ export const emailDeliveries = pgTable("email_deliveries", { emailId: uuid("email_id") .notNull() .references(() => sequenceEmails.id, { onDelete: "cascade" }), + /** The content this contact received; null for sends before versions. */ + contentVersionId: uuid("content_version_id").references( + () => emailContentVersions.id, + { onDelete: "set null" }, + ), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), }); diff --git a/apps/api/src/mail/campaign.ts b/apps/api/src/mail/campaign.ts index 661be12..c7a48dc 100644 --- a/apps/api/src/mail/campaign.ts +++ b/apps/api/src/mail/campaign.ts @@ -26,24 +26,35 @@ export interface CampaignEmailIds { emailId: string; } +/** What a web-version link opens: the email for one contact, as it was + * sent when `versionId` (an `email_content_versions.versionId`) is set. Links + * sent before versions existed have none and show the current content. */ +export interface CampaignViewIds extends CampaignEmailIds { + versionId?: string; +} + /** Web versions stay reachable for as long as an archive would. */ const VIEW_TOKEN_TTL = "3650d"; -export function getCampaignViewUrl(ids: CampaignEmailIds): string { +export function getCampaignViewUrl({ versionId, ...ids }: CampaignViewIds): string { return getViewInBrowserUrl( - generatePixelToken({ kind: "view", ...ids }, VIEW_TOKEN_TTL), + generatePixelToken( + { kind: "view", ...ids, ...(versionId && { v: versionId }) }, + VIEW_TOKEN_TTL, + ), ); } -export function verifyCampaignViewToken(token: string): CampaignEmailIds | null { - const payload = verifyPixelToken & { kind?: string }>( - token, - ); +export function verifyCampaignViewToken(token: string): CampaignViewIds | null { + const payload = verifyPixelToken< + Partial & { kind?: string; v?: unknown } + >(token); if ( payload?.kind !== "view" || typeof payload.contactId !== "string" || typeof payload.sequenceId !== "string" || - typeof payload.emailId !== "string" + typeof payload.emailId !== "string" || + (payload.v !== undefined && typeof payload.v !== "string") ) { return null; } @@ -51,6 +62,7 @@ export function verifyCampaignViewToken(token: string): CampaignEmailIds | null contactId: payload.contactId, sequenceId: payload.sequenceId, emailId: payload.emailId, + ...(payload.v && { versionId: payload.v }), }; } @@ -81,6 +93,7 @@ export async function renderCampaignHtml({ content, subscriber, ids, + contentVersionId, unsubscribeToken, web = false, publication, @@ -89,6 +102,9 @@ export async function renderCampaignHtml({ content: unknown; subscriber: { email: string; name: string | null; tags: string[] }; ids: CampaignEmailIds; + /** Public id of the stored copy of `content`, for the "view in browser" + * link (`sequences/content-versions.ts`). */ + contentVersionId?: string; unsubscribeToken: string; /** `"public"`: the published web page, with nobody's data and no * unsubscribe link. */ @@ -100,7 +116,9 @@ export async function renderCampaignHtml({ const publicUrl = publication ? issueUrl(settings, publication) : null; // The footer's "view in browser" link goes to the public page once the // issue is published, else to the reader's own web version. - const viewInBrowserUrl = web ? undefined : (publicUrl ?? getCampaignViewUrl(ids)); + const viewInBrowserUrl = web + ? undefined + : (publicUrl ?? getCampaignViewUrl({ ...ids, versionId: contentVersionId })); const isPublic = web === "public"; const variables = { subscriber: isPublic ? { email: "", name: null, tags: [] } : subscriber, diff --git a/apps/api/src/mcp/tools/sequences.ts b/apps/api/src/mcp/tools/sequences.ts index 4c6af99..355407d 100644 --- a/apps/api/src/mcp/tools/sequences.ts +++ b/apps/api/src/mcp/tools/sequences.ts @@ -173,7 +173,7 @@ export function registerSequenceTools(server: McpToolRegistrar): void { "update_sequence", { description: - "Updates a broadcast/sequence's title, trigger, audience filter, exclusion filter, or selected user ESP.", + "Updates a broadcast/sequence's title, trigger, audience filter, exclusion filter, or selected user ESP. A sent broadcast's title (its email's subject) can't be changed.", inputSchema: { sequenceId: z.string(), title: z.string().optional(), @@ -274,7 +274,7 @@ export function registerSequenceTools(server: McpToolRegistrar): void { "update_sequence_email", { description: - "Updates one email within a sequence/broadcast: subject, content, delay, publish status, or the tag action fired on send.", + 'Updates one email within a sequence/broadcast: subject, content, delay, publish status, or the tag action fired on send. Edits apply to later sends only; the "view in browser" page of an email already sent keeps showing what was sent. A sent broadcast can\'t be edited.', inputSchema: { sequenceId: z.string(), emailId: z.string(), @@ -321,7 +321,7 @@ export function registerSequenceTools(server: McpToolRegistrar): void { "send_sequence_email_test", { description: - "Sends a test of one broadcast/sequence email, rendered exactly like a real send (merge tags, footer, tracking) with a [Test] subject prefix. Defaults to the current user's own address (OAuth sessions only; API-key sessions must supply `to`). Suppressed addresses are skipped; opens, clicks and the unsubscribe link in a test change nothing. Every recipient counts as one test: by default 100 per team in any 24 hours (50 in any 3 hours) and 20 per email per 24 hours (errors `test_send_limit_reached`, `test_send_email_limit_reached`). Tests through an organization's shared mailbox also use the team's shared-delivery quota.", + "Sends a test of one broadcast/sequence email, rendered exactly like a real send (merge tags, footer, tracking) with a [Test] subject prefix; its \"view in browser\" link shows the email as tested. Defaults to the current user's own address (OAuth sessions only; API-key sessions must supply `to`). Suppressed addresses are skipped; opens, clicks and the unsubscribe link in a test change nothing. Every recipient counts as one test: by default 100 per team in any 24 hours (50 in any 3 hours) and 20 per email per 24 hours (errors `test_send_limit_reached`, `test_send_email_limit_reached`). Tests through an organization's shared mailbox also use the team's shared-delivery quota.", inputSchema: { sequenceId: z.string(), emailId: z.string(), diff --git a/apps/api/src/sequences/content-versions.test.ts b/apps/api/src/sequences/content-versions.test.ts new file mode 100644 index 0000000..e121621 --- /dev/null +++ b/apps/api/src/sequences/content-versions.test.ts @@ -0,0 +1,72 @@ +import { beforeEach, describe, expect, it, mock } from "bun:test"; +import { eq } from "drizzle-orm"; + +mock.module("../db/client", async () => { + const { makeTestDb } = await import("../test/db.js"); + return { db: await makeTestDb() }; +}); + +const { db } = await import("../db/client"); +const { emailContentVersions } = await import("../db/schema"); +const { seedTeamAndContact, truncateAll } = await import("../test/db"); +type TestDb = import("../test/db").TestDb; +const { emailContent, seedSequence } = await import("../test/fixtures"); +const { getContentVersion, getOrCreateContentVersion } = await import( + "./content-versions" +); + +const tdb = db as unknown as TestDb; + +beforeEach(async () => { + await truncateAll(tdb); +}); + +async function seed() { + const { team } = await seedTeamAndContact(tdb); + const { emailRows } = await seedSequence(tdb, { + teamId: team.id, + emails: [{ emailId: "email_e1" }, { emailId: "email_e2" }], + }); + return { team, emails: emailRows.map((email) => ({ ...email, teamId: team.id })) }; +} + +describe("getOrCreateContentVersion", () => { + it("stores each distinct content once and a new copy after an edit", async () => { + const { emails } = await seed(); + const [email] = emails; + + const first = await getOrCreateContentVersion(email); + const again = await getOrCreateContentVersion(email); + // Same content read back from Postgres (jsonb reorders keys). + const reread = await getOrCreateContentVersion({ + ...email, + content: JSON.parse(JSON.stringify(email.content)), + }); + expect(again).toEqual(first); + expect(reread).toEqual(first); + expect(first.versionId).toMatch(/^ver_/); + + const edited = await getOrCreateContentVersion({ + ...email, + content: emailContent({ text: "Edited" }), + }); + const renamed = await getOrCreateContentVersion({ ...email, subject: "New" }); + expect(new Set([first.id, edited.id, renamed.id]).size).toBe(3); + + const rows = await tdb + .select() + .from(emailContentVersions) + .where(eq(emailContentVersions.sequenceEmailId, email.id)); + expect(rows).toHaveLength(3); + const stored = await getContentVersion(email.id, edited.versionId); + expect(JSON.stringify(stored?.content)).toContain("Edited"); + }); + + it("finds a version only through its own email", async () => { + const { emails } = await seed(); + const version = await getOrCreateContentVersion(emails[0]); + + expect(await getContentVersion(emails[1].id, version.versionId)).toBeNull(); + expect(await getContentVersion(emails[0].id, "ver_missing")).toBeNull(); + }); +}); diff --git a/apps/api/src/sequences/content-versions.ts b/apps/api/src/sequences/content-versions.ts new file mode 100644 index 0000000..f220af7 --- /dev/null +++ b/apps/api/src/sequences/content-versions.ts @@ -0,0 +1,87 @@ +import { and, eq } from "drizzle-orm"; +import { contentHash } from "../abuse/content-scan"; +import { db } from "../db/client"; +import { emailContentVersions, type sequenceEmails } from "../db/schema"; + +export type EmailContentVersion = typeof emailContentVersions.$inferSelect; + +/** `id` is internal (for `email_deliveries`); `versionId` is public (for + * links). */ +export type ContentVersionRef = Pick; + +// A broadcast sends the same content to every recipient, so the version is +// remembered per content instead of looked up for each one. +const knownVersions = new Map(); +const KNOWN_VERSIONS_MAX = 1000; + +/** + * The stored copy of `email`'s current subject and content, created the + * first time this content is sent. Sends put its public `versionId` in their + * "view in browser" link so the page shows what was sent, not later edits. + */ +export async function getOrCreateContentVersion( + email: Pick & { + teamId: string; + }, +): Promise { + const hash = contentHash(email.subject, email.content); + const key = `${email.id}:${hash}`; + const known = knownVersions.get(key); + if (known) return known; + + const [inserted] = await db + .insert(emailContentVersions) + .values({ + teamId: email.teamId, + sequenceEmailId: email.id, + contentHash: hash, + subject: email.subject, + content: email.content, + }) + .onConflictDoNothing() + .returning({ + id: emailContentVersions.id, + versionId: emailContentVersions.versionId, + }); + const version = + inserted ?? + ( + await db + .select({ + id: emailContentVersions.id, + versionId: emailContentVersions.versionId, + }) + .from(emailContentVersions) + .where( + and( + eq(emailContentVersions.sequenceEmailId, email.id), + eq(emailContentVersions.contentHash, hash), + ), + ) + .limit(1) + )[0]; + if (!version) throw new Error("content_version_not_saved"); + + if (knownVersions.size >= KNOWN_VERSIONS_MAX) knownVersions.clear(); + knownVersions.set(key, version); + return version; +} + +/** The stored version with public id `versionId`, or null when it isn't a + * version of the email whose internal id is `sequenceEmailId`. */ +export async function getContentVersion( + sequenceEmailId: string, + versionId: string, +): Promise { + const [row] = await db + .select() + .from(emailContentVersions) + .where( + and( + eq(emailContentVersions.versionId, versionId), + eq(emailContentVersions.sequenceEmailId, sequenceEmailId), + ), + ) + .limit(1); + return row ?? null; +} diff --git a/apps/api/src/sequences/queries.test.ts b/apps/api/src/sequences/queries.test.ts index ba94fcb..59dee2f 100644 --- a/apps/api/src/sequences/queries.test.ts +++ b/apps/api/src/sequences/queries.test.ts @@ -181,6 +181,21 @@ describe("sequence queries", () => { subject: "Should not change", }); expect(result?.emails[0].subject).not.toBe("Should not change"); + // The title is the sent email's subject, so it is locked too. + await expect( + updateSequence({ + teamId: one.team.id, + sequenceId: broadcast.sequenceId, + title: "Should not change", + }), + ).rejects.toThrow(responses.mail_already_sent); + await expect( + updateSequence({ + teamId: one.team.id, + sequenceId: broadcast.sequenceId, + title: broadcast.title, + }), + ).resolves.toMatchObject({ title: broadcast.title }); await expect( addMailToSequence({ teamId: one.team.id, diff --git a/apps/api/src/sequences/queries.ts b/apps/api/src/sequences/queries.ts index 7401343..74ce057 100644 --- a/apps/api/src/sequences/queries.ts +++ b/apps/api/src/sequences/queries.ts @@ -299,6 +299,11 @@ export async function updateSequence({ }): Promise { const current = await getSequenceBySequenceId(teamId, sequenceId); if (!current) return null; + // A sent broadcast's title is its email's subject, which can't change + // after it went out (its content can't either; see updateMailInSequence). + if (title !== undefined && title !== current.title && isBroadcastLocked(current)) { + throw new Error(responses.mail_already_sent); + } const patch: Partial = { updatedAt: new Date() }; if (title !== undefined) patch.title = title; if (triggerType !== undefined) patch.triggerType = triggerType; diff --git a/apps/api/src/sequences/test-send.test.ts b/apps/api/src/sequences/test-send.test.ts index 2ba2d7c..266c58b 100644 --- a/apps/api/src/sequences/test-send.test.ts +++ b/apps/api/src/sequences/test-send.test.ts @@ -20,7 +20,9 @@ const { addOrStrengthenSuppression } = await import( const { sendMail } = await import("../mail/send"); const { seedTeamAndContact, truncateAll } = await import("../test/db"); type TestDb = import("../test/db").TestDb; -const { seedSequence } = await import("../test/fixtures"); +const { emailContent, seedSequence } = await import("../test/fixtures"); +const { verifyCampaignViewToken } = await import("../mail/campaign"); +const { getContentVersion } = await import("./content-versions"); const { TEST_CONTACT_ID } = await import("../config/constants"); const { verifyPixelToken } = await import("../utils/pixel-jwt"); const { sendSequenceEmailTest } = await import("./test-send"); @@ -80,6 +82,32 @@ describe("sendSequenceEmailTest", () => { expect(result.lastTestSentAt).toBe(row.lastTestSentAt?.toISOString() ?? null); }); + it("links a web version of the email as tested, not later edits", async () => { + const { team, sequenceRow, email } = await seed(); + + await sendSequenceEmailTest({ + teamId: team.id, + sequenceId: sequenceRow.sequenceId, + emailId: email.emailId, + to: ["me@example.com"], + }); + await tdb + .update(sequenceEmails) + .set({ content: emailContent({ text: "Edited after the test" }) }) + .where(eq(sequenceEmails.id, email.id)); + + const viewUrl = mockedSendMail.mock.calls[0][0].html.match( + /href="(https:\/\/sendlit\.test\/view\/[^"]+)"/, + )?.[1]; + const ids = verifyCampaignViewToken( + new URL(viewUrl ?? "http://x").pathname.slice(6), + ); + expect(ids?.contactId).toBe(TEST_CONTACT_ID); + const version = await getContentVersion(email.id, ids?.versionId ?? ""); + expect(JSON.stringify(version?.content)).toContain("Hello {{ subscriber.name }}!"); + expect(JSON.stringify(version?.content)).not.toContain("Edited after the test"); + }); + it("defaults to the fallback recipient and uses their contact data", async () => { const { team, contact, sequenceRow, email } = await seed(); await tdb diff --git a/apps/api/src/sequences/test-send.ts b/apps/api/src/sequences/test-send.ts index 774a229..8ecc364 100644 --- a/apps/api/src/sequences/test-send.ts +++ b/apps/api/src/sequences/test-send.ts @@ -22,6 +22,7 @@ import { assertMailingAddressConfigured } from "../settings/general/queries"; import { getTeam } from "../team/queries"; import { normalizeEmail } from "../utils/email"; import { getEmailFrom, getListUnsubscribeHeaders } from "../utils/mail"; +import { getOrCreateContentVersion } from "./content-versions"; import { takeTestSendQuota } from "./test-send-limits"; /** A test goes to at most this many addresses (Mailchimp allows a handful). */ @@ -153,6 +154,11 @@ export async function sendSequenceEmailTest({ } try { + // The test's "view in browser" link shows this copy, not later edits. + const contentVersion = + deliverable.length > 0 + ? await getOrCreateContentVersion({ ...email, teamId }) + : null; for (const recipient of deliverable) { const contact = await findContactByEmail(teamId, recipient); const html = await renderCampaignHtml({ @@ -168,6 +174,7 @@ export async function sendSequenceEmailTest({ sequenceId: sequence.sequenceId, emailId: email.emailId, }, + contentVersionId: contentVersion?.versionId, unsubscribeToken: TEST_UNSUBSCRIBE_TOKEN, }); await sendMail({ diff --git a/apps/api/src/tracking/web-view.test.ts b/apps/api/src/tracking/web-view.test.ts index ed9bdc1..64c61a8 100644 --- a/apps/api/src/tracking/web-view.test.ts +++ b/apps/api/src/tracking/web-view.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, mock } from "bun:test"; +import { eq } from "drizzle-orm"; import express from "express"; mock.module("../db/client", async () => { @@ -9,7 +10,9 @@ mock.module("../db/client", async () => { const { db } = await import("../db/client"); const { seedTeamAndContact, truncateAll } = await import("../test/db"); type TestDb = import("../test/db").TestDb; -const { seedSequence } = await import("../test/fixtures"); +const { emailContent, seedSequence } = await import("../test/fixtures"); +const { sequenceEmails } = await import("../db/schema"); +const { getOrCreateContentVersion } = await import("../sequences/content-versions"); const { requestApp } = await import("../test/http"); const { TEST_CONTACT_ID } = await import("../config/constants"); const { getCampaignViewUrl } = await import("../mail/campaign"); @@ -80,6 +83,59 @@ describe("GET /view/:token", () => { expect(response.body).toContain("/unsubscribe/test"); }); + it("shows the version that was sent, not later edits", async () => { + const { team, contact } = await seedTeamAndContact(tdb); + const { sequenceRow, emailRows } = await seedSequence(tdb, { + teamId: team.id, + emails: [{ emailId: "email_e1" }], + }); + const ids = { + contactId: contact.contactId, + sequenceId: sequenceRow.sequenceId, + emailId: emailRows[0].emailId, + }; + const sent = await getOrCreateContentVersion({ ...emailRows[0], teamId: team.id }); + await tdb + .update(sequenceEmails) + .set({ content: emailContent({ text: "Edited after sending" }) }) + .where(eq(sequenceEmails.id, emailRows[0].id)); + + const versioned = await requestApp( + app(), + pathOf(getCampaignViewUrl({ ...ids, versionId: sent.versionId })), + ); + expect(versioned.status).toBe(200); + expect(versioned.body).toContain("Hello Ada Lovelace!"); + expect(versioned.body).not.toContain("Edited after sending"); + + // Links sent before versions existed show the current content. + const legacy = await requestApp(app(), pathOf(getCampaignViewUrl(ids))); + expect(legacy.status).toBe(200); + expect(legacy.body).toContain("Edited after sending"); + }); + + it("404s a version that belongs to another email", async () => { + const { team, contact } = await seedTeamAndContact(tdb); + const { sequenceRow, emailRows } = await seedSequence(tdb, { + teamId: team.id, + emails: [{ emailId: "email_e1" }, { emailId: "email_e2" }], + }); + const other = await getOrCreateContentVersion({ ...emailRows[1], teamId: team.id }); + + const response = await requestApp( + app(), + pathOf( + getCampaignViewUrl({ + contactId: contact.contactId, + sequenceId: sequenceRow.sequenceId, + emailId: emailRows[0].emailId, + versionId: other.versionId, + }), + ), + ); + expect(response.status).toBe(404); + }); + it("404s tampered tokens, tracking tokens and other teams' contacts", async () => { const { team } = await seedTeamAndContact(tdb); const other = await seedTeamAndContact(tdb); diff --git a/apps/api/src/tracking/web-view.ts b/apps/api/src/tracking/web-view.ts index 6ea9994..65993df 100644 --- a/apps/api/src/tracking/web-view.ts +++ b/apps/api/src/tracking/web-view.ts @@ -6,6 +6,7 @@ import { db } from "../db/client"; import { sequenceEmails, sequences } from "../db/schema"; import { renderCampaignHtml, verifyCampaignViewToken } from "../mail/campaign"; import { captureError } from "../observability"; +import { getContentVersion } from "../sequences/content-versions"; import logger from "../services/log"; import { getTeam } from "../team/queries"; import { emailUnavailablePage } from "./unsubscribe-page"; @@ -24,7 +25,9 @@ const WEB_VIEW_CSP = [ ].join("; "); // The hosted "view in browser" version of one broadcast or sequence email, -// personalised for the recipient the signed link was sent to. +// personalised for the recipient the signed link was sent to. Links carry the +// version that was sent, so later edits don't change them; older links +// without one show the current content. router.get("/view/:token", async (req: Request, res: Response) => { res.setHeader("X-Robots-Tag", "noindex"); res.setHeader("Referrer-Policy", "no-referrer"); @@ -63,10 +66,16 @@ router.get("/view/:token", async (req: Request, res: Response) => { ) { return res.status(404).type("html").send(emailUnavailablePage()); } + const version = ids.versionId + ? await getContentVersion(email.id, ids.versionId) + : null; + if (ids.versionId && !version) { + return res.status(404).type("html").send(emailUnavailablePage()); + } const html = await renderCampaignHtml({ team, - content: email.content, + content: version ? version.content : email.content, subscriber: contact ? { email: contact.email, name: contact.name, tags: contact.tags } : { email: "", name: null, tags: [] }, diff --git a/apps/docs/content/docs/email-marketing/broadcasts.mdx b/apps/docs/content/docs/email-marketing/broadcasts.mdx index a970863..593dce7 100644 --- a/apps/docs/content/docs/email-marketing/broadcasts.mdx +++ b/apps/docs/content/docs/email-marketing/broadcasts.mdx @@ -27,6 +27,10 @@ web version shows their merge-tag data and their unsubscribe link. It has no open or click tracking and isn't indexed by search engines. To place the link elsewhere in your content, use the `{{ view_in_browser_url }}` merge tag. +The web version shows the email as it was sent. If you edit a sequence email +later, people who already got it still see the version they received, and a +test's link shows the email as you tested it. + Once a broadcast is [published to the web](/email-marketing/web-archive), the footer link goes to its public page instead, and so does the `{{ web_version_url }}` merge tag. Before that, `{{ web_version_url }}` is the diff --git a/packages/api-contract/src/contract.ts b/packages/api-contract/src/contract.ts index 9dc8431..107feec 100644 --- a/packages/api-contract/src/contract.ts +++ b/packages/api-contract/src/contract.ts @@ -494,6 +494,8 @@ const sequencesContract = c.router( 404: errorSchema, }, summary: "Update a broadcast or sequence", + description: + "A broadcast's title is its email's subject, so once the broadcast has been sent, changing the title returns 400 with the error \"This broadcast has already been sent\".", }, remove: { method: "DELETE", @@ -527,7 +529,7 @@ const sequencesContract = c.router( }, summary: "Update an email within a sequence", description: - "When templateId is supplied, it must identify a marketing template; transactional templates return 422 template_not_marketing.", + "When templateId is supplied, it must identify a marketing template; transactional templates return 422 template_not_marketing. Edits to an active sequence's email apply to later sends only: the \"view in browser\" page of an email already sent keeps showing what was sent. A sent broadcast can't be edited; the request returns it unchanged.", }, sendEmailTest: { method: "POST", @@ -541,7 +543,7 @@ const sequencesContract = c.router( }, summary: "Send a test of a broadcast or sequence email", description: - "Renders the email exactly like a real send (merge tags, footer, tracking) and sends it with a `[Test]` subject prefix through the delivery source the real send would use. Merge tags use the recipient's contact data when they are a contact. Opens, clicks and the unsubscribe link in a test change nothing. Suppressed addresses are skipped. Every recipient counts as one test: by default a team can send 100 test emails in any 24 hours (50 in any 3 hours) and 20 per email per 24 hours, after which this returns 429 `test_send_limit_reached` or `test_send_email_limit_reached`. Tests through an organization's shared mailbox also count against the team's shared-delivery quota (429 `organization_team_quota_exhausted` or `organization_quota_exhausted`).", + "Renders the email exactly like a real send (merge tags, footer, tracking) and sends it with a `[Test]` subject prefix through the delivery source the real send would use. Its \"view in browser\" link shows the email as it was when tested, not later edits. Merge tags use the recipient's contact data when they are a contact. Opens, clicks and the unsubscribe link in a test change nothing. Suppressed addresses are skipped. Every recipient counts as one test: by default a team can send 100 test emails in any 24 hours (50 in any 3 hours) and 20 per email per 24 hours, after which this returns 429 `test_send_limit_reached` or `test_send_email_limit_reached`. Tests through an organization's shared mailbox also count against the team's shared-delivery quota (429 `organization_team_quota_exhausted` or `organization_quota_exhausted`).", }, removeEmail: { method: "DELETE",