curl --request POST \
--url https://api.paigeme.dev/v1/broadcasts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"segment_id": "<string>",
"opted_in": true,
"tags": [
"<string>"
],
"clicked": {
"value": "<string>",
"since": "<string>"
},
"template_name": "<string>",
"template_id": "<string>",
"template_language": "<string>",
"variable_mapping": {},
"schedule": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
segment_id: '<string>',
opted_in: true,
tags: ['<string>'],
clicked: {value: '<string>', since: '<string>'},
template_name: '<string>',
template_id: '<string>',
template_language: '<string>',
variable_mapping: {},
schedule: '<string>'
})
};
fetch('https://api.paigeme.dev/v1/broadcasts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.paigeme.dev/v1/broadcasts"
payload = {
"name": "<string>",
"segment_id": "<string>",
"opted_in": True,
"tags": ["<string>"],
"clicked": {
"value": "<string>",
"since": "<string>"
},
"template_name": "<string>",
"template_id": "<string>",
"template_language": "<string>",
"variable_mapping": {},
"schedule": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"broadcast_id": "<string>",
"status": "<string>",
"template_approved": true,
"launch_blocked_reason": "<string>",
"summary": {},
"note": "<string>"
}
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}Assemble a broadcast for approval
Assembly NEVER sends. Guardrail: an assembled broadcast ALWAYS lands in pending_approval and can only go out via the dashboard approve flow. A MARKETING template requires an opted-in audience.
Beyond name, a valid call needs two things the schema below cannot mark required (they are checked by the assembler, which reports failures as 400):
- A template —
template_name(withtemplate_language) ortemplate_id. It must already be APPROVED by Meta. - An audience — either a saved
segment_id, or an inline filter built fromopted_in/tags/clicked. An audience that resolves to zero contacts is rejected.
Also supply variable_mapping whenever the template has variables; a mapping that does not match the template is rejected. schedule is genuinely optional — omit it to assemble for immediate approval.
A self-hosted project must be sent an audience its OWN backend can express (GRE-741). A saved segment whose only criterion is hand-picked contacts (include_ids) is refused with 400 PICKS_ONLY_UNSUPPORTED unless that backend advertised the include_only flag on its broadcast_capabilities handshake — the contract reads include_ids as a union on top of the filter, so “only these three” would otherwise reach the whole contact list. A multi-segment union is refused with 400 MULTI_SEGMENT_UNSUPPORTED (not reachable through this endpoint today, which takes a single segment_id). Both are the SAME refusal, with the same wording, that the dashboard applies at create and update, and both are decided here at write time rather than left to fail at dispatch days later. VM-hosted projects are unaffected.
Required scope: broadcasts:manage
curl --request POST \
--url https://api.paigeme.dev/v1/broadcasts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"segment_id": "<string>",
"opted_in": true,
"tags": [
"<string>"
],
"clicked": {
"value": "<string>",
"since": "<string>"
},
"template_name": "<string>",
"template_id": "<string>",
"template_language": "<string>",
"variable_mapping": {},
"schedule": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
segment_id: '<string>',
opted_in: true,
tags: ['<string>'],
clicked: {value: '<string>', since: '<string>'},
template_name: '<string>',
template_id: '<string>',
template_language: '<string>',
variable_mapping: {},
schedule: '<string>'
})
};
fetch('https://api.paigeme.dev/v1/broadcasts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.paigeme.dev/v1/broadcasts"
payload = {
"name": "<string>",
"segment_id": "<string>",
"opted_in": True,
"tags": ["<string>"],
"clicked": {
"value": "<string>",
"since": "<string>"
},
"template_name": "<string>",
"template_id": "<string>",
"template_language": "<string>",
"variable_mapping": {},
"schedule": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"broadcast_id": "<string>",
"status": "<string>",
"template_approved": true,
"launch_blocked_reason": "<string>",
"summary": {},
"note": "<string>"
}
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}{
"success": false,
"error": {
"code": "invalid_request",
"message": "Invalid request body",
"details": {}
},
"request_id": "<string>"
}Authorizations
Project API key issued in Settings → API keys. Send it as Authorization: Bearer pk_live_….
Headers
Target project id, for an MCP OAuth bearer (mcp_at_…) attached to more than one project — get ids from GET /v1/projects. Matched case-insensitively.
Omit it and a READ falls back to the connection's default project; a mutation (any non-GET) on a connection with 2+ projects is rejected with 400 project_required — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.
Scopes are checked against the SELECTED project only, never a union across the connection.
For a pk_ API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with 403 project_not_attached (a blank value is 400 invalid_project_header, as above).
Body
1 - 255Optional ISO 8601 datetime to send at. Must be in the future AND land on a 15-minute boundary (minutes 00, 15, 30 or 45, with zero seconds) — scheduled broadcasts are swept every 15 minutes, so a finer time cannot be honoured and anything else is rejected. Round to the nearest quarter hour before sending. Records the intended send time only — nothing sends until a human approves.
Response
Success.
true Hide child attributes
Hide child attributes
Always pending_approval — assembly never sends.
Whether Meta has approved the chosen template. False blocks launch.
Why this broadcast cannot be approved yet, or null when it is ready.
Audience count, cost estimate, template preview and resolved variable mapping.
Advisory note from the assembler, e.g. an opt-in caveat.
Was this page helpful?
