Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ decision `experimentRevision`, and closed Platform campaign shapes. SDK types
follow each change on covered operations; feature evaluation stays excluded. `cloud-api-supplements.json` pins the Graph-compatible
catalog and Flow encryption routes to the same commit.

API PR #377, merged in this snapshot, rejects invalid project-template
drafts with 400 before submission. The typed `MessagingClient.templates.submit`
method makes one network attempt, including when callers supply an idempotency
key or retry override; the route declares no replay contract.

The merged Campaigns failed-state follow-up updates the `campaign.failed`
reason example and the public archive/delete `409` descriptions. It changes no
operation fingerprint or SDK method mapping.
Expand Down
9 changes: 9 additions & 0 deletions packages/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,19 @@ const created = await messaging.templates.create("support", {
await messaging.templates.preview("support", created.data.data.id, {
values: { name: "Grace" },
});

await messaging.templates.submit("support", created.data.data.id, {
session: "number_123",
});
```

Keep this client on the server. Browser builders use an application-owned
route, such as `createTemplateBuilderRoute` from `@polymorfa/nextjs`.
Submission validates the saved definition before making one provider attempt.
A successful response records the submission; it does not establish approval.
If the request times out or loses its response, read the Number's template
catalog before deciding whether another submission is needed. Local preview
does not ask Meta to validate the template.

## Flow drafts and provider lifecycle

Expand Down
2 changes: 2 additions & 0 deletions packages/typescript/src/messaging/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class TemplatesResource {
});
}

/** Submit once. If the response is lost, read the Number's template catalog before another attempt. */
submit(
projectSlug: string,
templateId: string,
Expand All @@ -101,6 +102,7 @@ export class TemplatesResource {
path: `${templatePath(projectSlug, templateId)}/submit`,
body,
...options,
maxNetworkRetries: 0,
});
}
}
Expand Down
23 changes: 23 additions & 0 deletions packages/typescript/test/cloud-templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,29 @@ describe("Cloud templates", () => {
},
);

it("submits a project template draft to Meta once, even with a retry override", async () => {
const server = await startTestServer(() => ({
status: 500,
body: JSON.stringify({
error: { code: "internal_error", message: "Outcome unknown" },
}),
}));
servers.push(server);
await expect(
client(server).templates.submit(
"support",
"tpl/1",
{ session: "number_123" },
{ idempotencyKey: "customer-key", maxNetworkRetries: 3 },
),
).rejects.toMatchObject({ status: 500 });
expect(server.requests).toHaveLength(1);
expect(server.requests[0]!.method).toBe("POST");
expect(server.requests[0]!.path).toBe(
"/messaging/projects/support/templates/tpl%2F1/submit",
);
});

it("returns a catalog failure instead of an empty successful list", async () => {
const server = await startTestServer(() => ({
status: 502,
Expand Down
Loading