> For the complete documentation index, see [llms.txt](https://movemint.gitbook.io/movemint-developer-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://movemint.gitbook.io/movemint-developer-docs/open-api-specification/core-resources/emails.md).

# Emails

Build email campaigns for an event or group — everything an organizer can do in the campaign builder **except sending**. Create a draft, set its metadata and recipients (audiences), build the block-based body, and preview the rendered HTML. There is no send/schedule/test endpoint, so a created email stays in `draft` status.

Emails are scoped to their sender: create them under `/api/v1/events/{handle}/emails` (sent from the event) or `/api/v1/groups/{group_handle}/emails` (sent from the group). Platform (Movemint-wide) sends are out of scope for this API.

## List event emails

> Returns the event's emails, newest first. Optionally filter by \`status\`.

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"EmailStatusFilter":{"name":"status","in":"query","required":false,"description":"Filter by send status.","schema":{"type":"string","enum":["draft","scheduled","sent","sending"]}}},"schemas":{"EmailListResult":{"type":"object","required":["emails"],"properties":{"emails":{"type":"array","items":{"$ref":"#/components/schemas/Email"}}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/emails":{"get":{"tags":["Emails"],"summary":"List event emails","description":"Returns the event's emails, newest first. Optionally filter by `status`.","operationId":"listEventEmails","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/EmailStatusFilter"}],"responses":{"200":{"description":"List of emails (summary shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailListResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"}}}}}}
````

## Create a draft email

> Creates a draft email sent from the event. The email starts in \`draft\`\
> status and is never sent by this API. Body content is set separately via\
> the design endpoint.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"schemas":{"CreateEmailRequest":{"allOf":[{"$ref":"#/components/schemas/EmailWritableFields"}]},"EmailWritableFields":{"type":"object","description":"Fields accepted by email create/update. On update, only keys present in\nthe request are applied — omitted fields are left untouched. `email_type`\nis honored on create only. The sender (`send_from`) is always forced to\nthe event/group in the URL and is never client-settable.\n","properties":{"name":{"type":"string","description":"Internal campaign name (max 255 chars). Defaults to a dated name on create."},"subject":{"type":"string","description":"Email subject line (max 255 chars)."},"preview_text":{"type":"string","description":"Preheader / preview text (max 255 chars)."},"email_type":{"type":"string","enum":["marketing","transactional"],"description":"Create-only. Defaults to `marketing`."},"send_from_athlete":{"type":"boolean","description":"Whether to include the sending admin's name in the from-name."},"send_to_future_audience_members":{"type":"boolean","description":"Auto-send to people who later match the audience (applies at send time only)."},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceInput"}}}},"EmailAudienceInput":{"type":"object","required":["type","id"],"description":"A recipient-audience reference to attach to (or exclude from) the email.\nValid `type`s depend on the sender scope: **event** emails accept\n`config` (a pre-built audience — see `available_audiences`); **group**\nemails accept `crm_audience` (a saved CRM audience id). Both scopes\naccept `filter_context` (the id of a custom audience previously created\nvia the custom_audience endpoint and already attached to this email).\nA ref that is unknown, of a type this sender scope doesn't accept, or\nowned by another sender is rejected with `422` and the whole request is\napplied atomically — nothing changes unless every ref is usable.\nUnknown and out-of-scope refs report identically, so a caller can't use\nthe error to probe which ids exist under a sender they can't see.\n","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context"]},"id":{"description":"The audience id. A string configuration key for `config`, an integer\nid for `crm_audience` / `filter_context`.\n","oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean","description":"True to include the audience, false to exclude it. Defaults to true."}}},"EmailWriteResult":{"type":"object","required":["email","audience_calculation"],"properties":{"email":{"$ref":"#/components/schemas/Email"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/events/{handle}/emails":{"post":{"tags":["Emails"],"summary":"Create a draft email","description":"Creates a draft email sent from the event. The email starts in `draft`\nstatus and is never sent by this API. Body content is set separately via\nthe design endpoint.\n","operationId":"createEventEmail","parameters":[{"$ref":"#/components/parameters/EventHandle"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmailRequest"}}}},"responses":{"201":{"description":"Draft email created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailWriteResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## GET /api/v1/events/{handle}/emails/{id}

> Get an event email

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailResult":{"type":"object","required":["email"],"properties":{"email":{"$ref":"#/components/schemas/Email"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/emails/{id}":{"get":{"tags":["Emails"],"summary":"Get an event email","operationId":"getEventEmail","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Email details (full shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"}}}}}}
````

## Update a draft email

> Partially updates the email's metadata and/or audiences. Only fields\
> present in the request are applied. Returns \`422\` if the email is no\
> longer editable (already sent/sending).<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"UpdateEmailRequest":{"$ref":"#/components/schemas/EmailWritableFields"},"EmailWritableFields":{"type":"object","description":"Fields accepted by email create/update. On update, only keys present in\nthe request are applied — omitted fields are left untouched. `email_type`\nis honored on create only. The sender (`send_from`) is always forced to\nthe event/group in the URL and is never client-settable.\n","properties":{"name":{"type":"string","description":"Internal campaign name (max 255 chars). Defaults to a dated name on create."},"subject":{"type":"string","description":"Email subject line (max 255 chars)."},"preview_text":{"type":"string","description":"Preheader / preview text (max 255 chars)."},"email_type":{"type":"string","enum":["marketing","transactional"],"description":"Create-only. Defaults to `marketing`."},"send_from_athlete":{"type":"boolean","description":"Whether to include the sending admin's name in the from-name."},"send_to_future_audience_members":{"type":"boolean","description":"Auto-send to people who later match the audience (applies at send time only)."},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceInput"}}}},"EmailAudienceInput":{"type":"object","required":["type","id"],"description":"A recipient-audience reference to attach to (or exclude from) the email.\nValid `type`s depend on the sender scope: **event** emails accept\n`config` (a pre-built audience — see `available_audiences`); **group**\nemails accept `crm_audience` (a saved CRM audience id). Both scopes\naccept `filter_context` (the id of a custom audience previously created\nvia the custom_audience endpoint and already attached to this email).\nA ref that is unknown, of a type this sender scope doesn't accept, or\nowned by another sender is rejected with `422` and the whole request is\napplied atomically — nothing changes unless every ref is usable.\nUnknown and out-of-scope refs report identically, so a caller can't use\nthe error to probe which ids exist under a sender they can't see.\n","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context"]},"id":{"description":"The audience id. A string configuration key for `config`, an integer\nid for `crm_audience` / `filter_context`.\n","oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean","description":"True to include the audience, false to exclude it. Defaults to true."}}},"EmailWriteResult":{"type":"object","required":["email","audience_calculation"],"properties":{"email":{"$ref":"#/components/schemas/Email"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/events/{handle}/emails/{id}":{"patch":{"tags":["Emails"],"summary":"Update a draft email","description":"Partially updates the email's metadata and/or audiences. Only fields\npresent in the request are applied. Returns `422` if the email is no\nlonger editable (already sent/sending).\n","operationId":"updateEventEmail","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailRequest"}}}},"responses":{"200":{"description":"Email updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailWriteResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## Set an email's body design

> Replaces the email body with a block-builder document, compiling it to\
> the HTML that would ship. Returns \`422\` with a list of \`errors\` if the\
> document fails schema/format validation.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailDesignRequest":{"type":"object","description":"The email body as a block-builder document. Provide the document as a\nnative JSON object under `design`, or as a JSON string under\n`blocks_json`. The document shape is validated server-side (rows /\ncolumns / typed blocks, hex colors, safe URLs) and compiled to the\nrendered HTML that would ship.\n","properties":{"design":{"type":"object","description":"The block document (version, document settings, rows).","additionalProperties":true},"blocks_json":{"type":"string","description":"The block document serialized as a JSON string. Alternative to `design`."}}},"EmailResult":{"type":"object","required":["email"],"properties":{"email":{"$ref":"#/components/schemas/Email"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/events/{handle}/emails/{id}/design":{"put":{"tags":["Emails"],"summary":"Set an email's body design","description":"Replaces the email body with a block-builder document, compiling it to\nthe HTML that would ship. Returns `422` with a list of `errors` if the\ndocument fails schema/format validation.\n","operationId":"setEventEmailDesign","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailDesignRequest"}}}},"responses":{"200":{"description":"Design applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## GET /api/v1/events/{handle}/emails/{id}/preview

> Preview an email's rendered HTML

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailPreviewResult":{"type":"object","required":["rendered_html","preview_text"],"properties":{"rendered_html":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/emails/{id}/preview":{"get":{"tags":["Emails"],"summary":"Preview an email's rendered HTML","operationId":"previewEventEmail","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Rendered HTML (null before any content is set)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreviewResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"}}}}}}
````

## Create a custom recipient audience

> Builds a custom audience from CRM-style filters and attaches it to the\
> email. Materializes asynchronously — poll \`audience\_calculation\`.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailCustomAudienceRequest":{"type":"object","description":"Creates a custom recipient audience from CRM-style filters and attaches\nit to the email. The audience materializes asynchronously — poll the\nreturned `audience_calculation.calculating` flag.\n","properties":{"include":{"type":"boolean","description":"Include (true, default) or exclude (false) the matched recipients."},"filters":{"type":"object","description":"CRM-style filter selection (e.g. `{ \"entity_types\": [\"EventParticipants\"],\n\"genders\": [\"Male\"] }`). Must resolve to at least one real filter.\n","additionalProperties":true}}},"EmailCustomAudienceResult":{"type":"object","required":["audience","audience_calculation"],"properties":{"audience":{"$ref":"#/components/schemas/EmailAudienceRef"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/emails/{id}/custom_audience":{"post":{"tags":["Emails"],"summary":"Create a custom recipient audience","description":"Builds a custom audience from CRM-style filters and attaches it to the\nemail. Materializes asynchronously — poll `audience_calculation`.\n","operationId":"createEventEmailCustomAudience","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCustomAudienceRequest"}}}},"responses":{"201":{"description":"Audience created and attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCustomAudienceResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"description":"Empty/unresolvable filters, or the email is not editable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## List group emails

> Returns the group's emails, newest first. Optionally filter by \`status\`.

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"EmailStatusFilter":{"name":"status","in":"query","required":false,"description":"Filter by send status.","schema":{"type":"string","enum":["draft","scheduled","sent","sending"]}}},"schemas":{"EmailListResult":{"type":"object","required":["emails"],"properties":{"emails":{"type":"array","items":{"$ref":"#/components/schemas/Email"}}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GroupNotFound":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails":{"get":{"tags":["Emails"],"summary":"List group emails","description":"Returns the group's emails, newest first. Optionally filter by `status`.","operationId":"listGroupEmails","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/EmailStatusFilter"}],"responses":{"200":{"description":"List of emails (summary shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailListResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/GroupNotFound"}}}}}}
````

## Create a draft group email

> Creates a draft email sent from the group. Stays in \`draft\`; never sent by this API.

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}}},"schemas":{"CreateEmailRequest":{"allOf":[{"$ref":"#/components/schemas/EmailWritableFields"}]},"EmailWritableFields":{"type":"object","description":"Fields accepted by email create/update. On update, only keys present in\nthe request are applied — omitted fields are left untouched. `email_type`\nis honored on create only. The sender (`send_from`) is always forced to\nthe event/group in the URL and is never client-settable.\n","properties":{"name":{"type":"string","description":"Internal campaign name (max 255 chars). Defaults to a dated name on create."},"subject":{"type":"string","description":"Email subject line (max 255 chars)."},"preview_text":{"type":"string","description":"Preheader / preview text (max 255 chars)."},"email_type":{"type":"string","enum":["marketing","transactional"],"description":"Create-only. Defaults to `marketing`."},"send_from_athlete":{"type":"boolean","description":"Whether to include the sending admin's name in the from-name."},"send_to_future_audience_members":{"type":"boolean","description":"Auto-send to people who later match the audience (applies at send time only)."},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceInput"}}}},"EmailAudienceInput":{"type":"object","required":["type","id"],"description":"A recipient-audience reference to attach to (or exclude from) the email.\nValid `type`s depend on the sender scope: **event** emails accept\n`config` (a pre-built audience — see `available_audiences`); **group**\nemails accept `crm_audience` (a saved CRM audience id). Both scopes\naccept `filter_context` (the id of a custom audience previously created\nvia the custom_audience endpoint and already attached to this email).\nA ref that is unknown, of a type this sender scope doesn't accept, or\nowned by another sender is rejected with `422` and the whole request is\napplied atomically — nothing changes unless every ref is usable.\nUnknown and out-of-scope refs report identically, so a caller can't use\nthe error to probe which ids exist under a sender they can't see.\n","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context"]},"id":{"description":"The audience id. A string configuration key for `config`, an integer\nid for `crm_audience` / `filter_context`.\n","oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean","description":"True to include the audience, false to exclude it. Defaults to true."}}},"EmailWriteResult":{"type":"object","required":["email","audience_calculation"],"properties":{"email":{"$ref":"#/components/schemas/Email"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GroupNotFound":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails":{"post":{"tags":["Emails"],"summary":"Create a draft group email","description":"Creates a draft email sent from the group. Stays in `draft`; never sent by this API.","operationId":"createGroupEmail","parameters":[{"$ref":"#/components/parameters/GroupHandle"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmailRequest"}}}},"responses":{"201":{"description":"Draft email created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailWriteResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/GroupNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## GET /api/v1/groups/{group\_handle}/emails/{id}

> Get a group email

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailResult":{"type":"object","required":["email"],"properties":{"email":{"$ref":"#/components/schemas/Email"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails/{id}":{"get":{"tags":["Emails"],"summary":"Get a group email","operationId":"getGroupEmail","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Email details (full shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"}}}}}}
````

## PATCH /api/v1/groups/{group\_handle}/emails/{id}

> Update a draft group email

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"UpdateEmailRequest":{"$ref":"#/components/schemas/EmailWritableFields"},"EmailWritableFields":{"type":"object","description":"Fields accepted by email create/update. On update, only keys present in\nthe request are applied — omitted fields are left untouched. `email_type`\nis honored on create only. The sender (`send_from`) is always forced to\nthe event/group in the URL and is never client-settable.\n","properties":{"name":{"type":"string","description":"Internal campaign name (max 255 chars). Defaults to a dated name on create."},"subject":{"type":"string","description":"Email subject line (max 255 chars)."},"preview_text":{"type":"string","description":"Preheader / preview text (max 255 chars)."},"email_type":{"type":"string","enum":["marketing","transactional"],"description":"Create-only. Defaults to `marketing`."},"send_from_athlete":{"type":"boolean","description":"Whether to include the sending admin's name in the from-name."},"send_to_future_audience_members":{"type":"boolean","description":"Auto-send to people who later match the audience (applies at send time only)."},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceInput"}}}},"EmailAudienceInput":{"type":"object","required":["type","id"],"description":"A recipient-audience reference to attach to (or exclude from) the email.\nValid `type`s depend on the sender scope: **event** emails accept\n`config` (a pre-built audience — see `available_audiences`); **group**\nemails accept `crm_audience` (a saved CRM audience id). Both scopes\naccept `filter_context` (the id of a custom audience previously created\nvia the custom_audience endpoint and already attached to this email).\nA ref that is unknown, of a type this sender scope doesn't accept, or\nowned by another sender is rejected with `422` and the whole request is\napplied atomically — nothing changes unless every ref is usable.\nUnknown and out-of-scope refs report identically, so a caller can't use\nthe error to probe which ids exist under a sender they can't see.\n","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context"]},"id":{"description":"The audience id. A string configuration key for `config`, an integer\nid for `crm_audience` / `filter_context`.\n","oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean","description":"True to include the audience, false to exclude it. Defaults to true."}}},"EmailWriteResult":{"type":"object","required":["email","audience_calculation"],"properties":{"email":{"$ref":"#/components/schemas/Email"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails/{id}":{"patch":{"tags":["Emails"],"summary":"Update a draft group email","operationId":"updateGroupEmail","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmailRequest"}}}},"responses":{"200":{"description":"Email updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailWriteResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## PUT /api/v1/groups/{group\_handle}/emails/{id}/design

> Set a group email's body design

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailDesignRequest":{"type":"object","description":"The email body as a block-builder document. Provide the document as a\nnative JSON object under `design`, or as a JSON string under\n`blocks_json`. The document shape is validated server-side (rows /\ncolumns / typed blocks, hex colors, safe URLs) and compiled to the\nrendered HTML that would ship.\n","properties":{"design":{"type":"object","description":"The block document (version, document settings, rows).","additionalProperties":true},"blocks_json":{"type":"string","description":"The block document serialized as a JSON string. Alternative to `design`."}}},"EmailResult":{"type":"object","required":["email"],"properties":{"email":{"$ref":"#/components/schemas/Email"}}},"Email":{"type":"object","required":["id","name","email_type","subject","preview_text","send_status","send_from","send_from_athlete","send_to_future_audience_members","editable","has_content","created_at","updated_at"],"description":"An email campaign. Summary fields are always present; the detailed\nfields (`audiences`, `design`, `rendered_html`, `sendable_blockers`,\n`available_audiences`) are returned by show/create/update/design but\nomitted from the list endpoint.\n","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string","nullable":true},"email_type":{"type":"string","enum":["marketing","transactional"]},"subject":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true},"send_status":{"type":"string","enum":["draft","scheduled","sent","sending"]},"send_from":{"type":"object","required":["scope","id"],"properties":{"scope":{"type":"string","enum":["event","group"]},"id":{"type":"integer","format":"int64"}}},"send_from_athlete":{"type":"boolean","nullable":true},"send_to_future_audience_members":{"type":"boolean"},"audiences":{"type":"array","items":{"$ref":"#/components/schemas/EmailAudienceRef"}},"editable":{"type":"boolean","description":"True while the email can still be edited (draft or scheduled)."},"has_content":{"type":"boolean","description":"Whether the email has rendered body content."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"design":{"type":"object","nullable":true,"description":"The block document (detailed responses only).","additionalProperties":true},"rendered_html":{"type":"string","nullable":true,"description":"Compiled HTML (detailed responses only)."},"sendable_blockers":{"type":"object","description":"Read-only map of field name → reason code describing what is still\nmissing before the email could ever be scheduled. Keys are the\nrequest field they refer to (`subject`, `send_from`,\n`send_from_athlete`, `audiences`, `rendered_html`); an empty object\nmeans nothing is outstanding. Codes are stable — `missing` for an\nunset field, `calculating` while an attached audience is still\nmaterializing (retry shortly). Present on detailed responses; does\nnot enable sending.\n","additionalProperties":{"type":"string","enum":["missing","calculating"]}},"available_audiences":{"type":"array","description":"Selectable audience sections for this sender scope (detailed responses only).","items":{"type":"object","additionalProperties":true}}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailUnprocessable":{"description":"The request could not be processed: either the email is no longer\neditable (already sent/sending) or a payload failed validation. The\nnot-editable and render/parse cases return the single-string `error`\nshape; model validation failures return the `errors` array shape.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/ValidationError"}]}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails/{id}/design":{"put":{"tags":["Emails"],"summary":"Set a group email's body design","operationId":"setGroupEmailDesign","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailDesignRequest"}}}},"responses":{"200":{"description":"Design applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"$ref":"#/components/responses/EmailUnprocessable"}}}}}}
````

## GET /api/v1/groups/{group\_handle}/emails/{id}/preview

> Preview a group email's rendered HTML

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailPreviewResult":{"type":"object","required":["rendered_html","preview_text"],"properties":{"rendered_html":{"type":"string","nullable":true},"preview_text":{"type":"string","nullable":true}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails/{id}/preview":{"get":{"tags":["Emails"],"summary":"Preview a group email's rendered HTML","operationId":"previewGroupEmail","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Rendered HTML (null before any content is set)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreviewResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"}}}}}}
````

## POST /api/v1/groups/{group\_handle}/emails/{id}/custom\_audience

> Create a custom recipient audience for a group email

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Emails","description":"Build email campaigns for an event or group — everything an organizer can\ndo in the campaign builder **except sending**. Create a draft, set its\nmetadata and recipients (audiences), build the block-based body, and\npreview the rendered HTML. There is no send/schedule/test endpoint, so a\ncreated email stays in `draft` status.\n\nEmails are scoped to their sender: create them under\n`/api/v1/events/{handle}/emails` (sent from the event) or\n`/api/v1/groups/{group_handle}/emails` (sent from the group). Platform\n(Movemint-wide) sends are out of scope for this API.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"GroupHandle":{"name":"group_handle","in":"path","required":true,"description":"The unique handle (slug) of the group","schema":{"type":"string"}},"ResourceId":{"name":"id","in":"path","required":true,"description":"The ID of the resource","schema":{"type":"integer","format":"int64"}}},"schemas":{"EmailCustomAudienceRequest":{"type":"object","description":"Creates a custom recipient audience from CRM-style filters and attaches\nit to the email. The audience materializes asynchronously — poll the\nreturned `audience_calculation.calculating` flag.\n","properties":{"include":{"type":"boolean","description":"Include (true, default) or exclude (false) the matched recipients."},"filters":{"type":"object","description":"CRM-style filter selection (e.g. `{ \"entity_types\": [\"EventParticipants\"],\n\"genders\": [\"Male\"] }`). Must resolve to at least one real filter.\n","additionalProperties":true}}},"EmailCustomAudienceResult":{"type":"object","required":["audience","audience_calculation"],"properties":{"audience":{"$ref":"#/components/schemas/EmailAudienceRef"},"audience_calculation":{"$ref":"#/components/schemas/AudienceCalculation"}}},"EmailAudienceRef":{"type":"object","required":["type","id","include"],"description":"A recipient audience currently attached to the email.","properties":{"type":{"type":"string","enum":["config","crm_audience","filter_context","platform_segment"]},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"include":{"type":"boolean"}}},"AudienceCalculation":{"type":"object","required":["calculating","pending_audience_ids"],"description":"Async materialization status for the email's audiences.","properties":{"calculating":{"type":"boolean"},"pending_audience_ids":{"type":"array","items":{"type":"integer"}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EmailNotFound":{"description":"Event/group or email not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/groups/{group_handle}/emails/{id}/custom_audience":{"post":{"tags":["Emails"],"summary":"Create a custom recipient audience for a group email","operationId":"createGroupEmailCustomAudience","parameters":[{"$ref":"#/components/parameters/GroupHandle"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCustomAudienceRequest"}}}},"responses":{"201":{"description":"Audience created and attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCustomAudienceResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EmailNotFound"},"422":{"description":"Empty/unresolvable filters, or the email is not editable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````
