Implement governed hybrid campaign delivery

This commit is contained in:
2026-08-02 13:58:37 +02:00
parent b38597f2be
commit 7733265cc8
40 changed files with 2531 additions and 122 deletions
+60
View File
@@ -368,6 +368,39 @@ export type CampaignPostboxCatalog = {
organization_units: CampaignPostboxOrganizationUnit[];
};
export type CampaignPrintTemplateOutputProfile = {
id: string;
label: string;
output_format: "html" | "text";
media_type: string;
channel: string;
capabilities: string[];
page: Record<string, unknown>;
};
export type CampaignPrintTemplate = {
id: string;
name: string;
slug: string;
template_type: string;
status: string;
current_revision: number;
current_revision_id: string;
published_revision_id?: string | null;
description?: string | null;
read_only: boolean;
revision?: {
revision: number;
output_profiles: CampaignPrintTemplateOutputProfile[];
required_fields: Array<{path: string;label?: string | null;required: boolean;}>;
} | null;
};
export type CampaignPrintTemplatesResponse = {
available: boolean;
templates: CampaignPrintTemplate[];
};
export type CampaignRecipientSnapshotItem = {
contact_id: string;
display_name: string;
@@ -494,6 +527,8 @@ export type CampaignSummary = {
needs_attention?: number;
sent?: number;
smtp_accepted?: number;
postbox_accepted?: number;
print_accepted?: number;
failed?: number;
outcome_unknown?: number;
not_attempted?: number;
@@ -729,6 +764,7 @@ export type CampaignJobDetailResponse = {
smtp?: Record<string, unknown>[];
imap?: Record<string, unknown>[];
postbox?: Record<string, unknown>[];
print?: Record<string, unknown>[];
};
};
@@ -771,6 +807,7 @@ export type AggregateCampaignReport = {
outcomes: {
smtp_accepted: AggregateReportCount;
postbox_accepted: AggregateReportCount;
print_accepted: AggregateReportCount;
delivered: AggregateReportCount;
partially_accepted: AggregateReportCount;
failed: AggregateReportCount;
@@ -908,6 +945,15 @@ campaignId: string)
return apiFetch<CampaignPostboxCatalog>(settings, `/api/v1/campaigns/${campaignId}/postbox-catalog`);
}
export async function listCampaignPrintTemplates(
settings: ApiSettings,
campaignId: string,
query = "")
: Promise<CampaignPrintTemplatesResponse> {
const suffix = query.trim() ? `?query=${encodeURIComponent(query.trim())}` : "";
return apiFetch<CampaignPrintTemplatesResponse>(settings, `/api/v1/campaigns/${campaignId}/print-templates${suffix}`);
}
export async function snapshotCampaignRecipientAddressSource(
settings: ApiSettings,
campaignId: string,
@@ -1242,6 +1288,20 @@ versionId?: string)
);
}
export async function downloadCampaignPrintArtifact(
settings: ApiSettings,
downloadPath: string,
filename: string)
: Promise<void> {
if (![
"/api/v1/campaigns/",
"/api/v1/files/"
].some((prefix) => downloadPath.startsWith(prefix))) {
throw new Error("The printable output download path is invalid.");
}
await apiDownload(settings, downloadPath, filename || "campaign-print-output.html");
}
export async function emailCampaignReport(
settings: ApiSettings,
campaignId: string,