Create a sign-up page

POST /api/v1/signup-pages Creates a sign-up page. Answers 201 with the created page — its id is what every other sign-up page call takes. Without status, the page is a draft: it answers a not-found page at its address and collects nothing until you publish it. Provisioning never puts an address online by accident.

Authorization

Role in the organizationThis endpoint
OWNERAllowed
ADMINAllowed
MEMBERRefused — 403
A key carries the role its creator holds in this organization — never a role on AgentsMail itself. See API keys.

Request body

FieldTypeRequiredDescription
namestringyesInternal name, 1 to 100 characters. Only your team sees it
slugstringyesLast part of the public address. 2 to 100 characters, lowercase letters, digits and hyphens only. Unique in the organization
listIdstringyesThe list that receives the subscribers. Must belong to your organization
contentstringyesThe HTML of the page. Must carry {{form}}
heroTitlestringyesThe title visitors read, up to 200 characters
submitLabelstringyesLabel of the sign-up button, up to 60 characters
successMessagestringyesShown after a sign-up when there is no redirect, up to 300 characters
sourceTemplateNamestringnoThe theme the HTML was copied from, up to 100 characters
heroSubtitlestringnoSubtitle under the title, up to 300 characters
redirectUrlstringnohttp:// or https:// address to send the visitor to instead of showing successMessage
collectFirstNamebooleannotrue by default
collectLastNamebooleannofalse by default
statusstringnodraft (default) or published
json
{
  "name": "Newsletter footer",
  "slug": "newsletter",
  "listId": "9f8e7d6c-1111-4bbb-8ccc-ddddeeeeffff",
  "content": "<html><body><h1>{{heroTitle}}</h1>{{form}}{{poweredBy}}</body></html>",
  "heroTitle": "Join the list",
  "submitLabel": "Sign me up",
  "successMessage": "Check your inbox.",
  "status": "published"
}

Example

bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"name":"Newsletter footer","slug":"newsletter","listId":"'$LIST_ID'","content":"<html><body>{{form}}</body></html>","heroTitle":"Join the list","submitLabel":"Sign me up","successMessage":"Check your inbox."}' \
  "https://www.agentsmail.io/api/v1/signup-pages"

Response

  • 201 Created — the created page, content included.
  • 400 Bad Request — a required field is missing or malformed, or the HTML carries no {{form}}. When the shape is at fault, details carries the field-level issues.
  • 401 Unauthorized — missing or invalid key, or its owner left the organization.
  • 403 Forbidden — the key's owner is a member of the organization, not an admin.
  • 404 Not FoundlistId names no list of your organization. Unknown and not-yours answer the same thing.
  • 409 Conflict — another page already holds that slug; the message names it. Nothing was created.
  • 429 Too Many Requests — the key is over its rate limit.

Response fields

FieldTypeDescription
successbooleanIndicates if the operation was successful
data.idstringUnique identifier for the page
data.namestringInternal name
data.slugstringLast part of the public address
data.statusstringdraft or published
data.listIdstringThe list that receives the subscribers
data.contentstringThe HTML of the page
data.sourceTemplateNamestring | nullThe theme it was copied from, null for pasted HTML
data.heroTitlestringThe title visitors read
data.heroSubtitlestring | nullThe subtitle, null when unset
data.submitLabelstringLabel of the sign-up button
data.successMessagestringShown after a sign-up when there is no redirect
data.redirectUrlstring | nullWhere the visitor goes after signing up, null for none
data.collectFirstNamebooleanWhether the form asks for a first name
data.collectLastNamebooleanWhether the form asks for a last name
data.createdAtstringISO creation date
data.updatedAtstringISO date of the last change

Example response

json
{
  "success": true,
  "data": {
    "id": "3d3d3d3d-4444-4eee-9fff-000011112222",
    "name": "Newsletter footer",
    "slug": "newsletter",
    "status": "published",
    "listId": "9f8e7d6c-1111-4bbb-8ccc-ddddeeeeffff",
    "content": "<html><body>{{form}}</body></html>",
    "sourceTemplateName": null,
    "heroTitle": "Join the list",
    "heroSubtitle": null,
    "submitLabel": "Sign me up",
    "successMessage": "Check your inbox.",
    "redirectUrl": null,
    "collectFirstName": true,
    "collectLastName": false,
    "createdAt": "2026-08-23T10:22:05.000Z",
    "updatedAt": "2026-08-23T10:22:05.000Z"
  }
}

Agent recipe

Copy a theme, then publish once the address is confirmed — three calls, in this order:
js
const themes = (await get('/api/v1/signup-page-templates')).data
const theme = themes[0]

const page = (
  await post('/api/v1/signup-pages', {
    name: 'Newsletter footer',
    slug: 'newsletter',
    listId,
    content: theme.content,
    sourceTemplateName: theme.name,
    heroTitle: 'Join the list',
    submitLabel: 'Sign me up',
    successMessage: 'Check your inbox.',
  })
).data

// The page is still a draft here: nothing answers at its address yet
await patch(`/api/v1/signup-pages/${page.id}`, {status: 'published'})
A 409 on the second call means the slug is taken. Pick another one — retrying will not help.

Notes

  • The HTML is a copy, never a link. Changing the theme in the catalogue later leaves your page exactly as it is.
  • redirectUrl and successMessage are not alternatives you both set: a page with a redirect never shows the message.