{
  "openapi": "3.1.0",
  "info": {
    "title": "Envoke API",
    "version": "1.0.0",
    "description": "Merged OpenAPI description of every published Envoke REST API. Paths include the API version (/v1 or /v2). Authentication is HTTP Basic Auth using your API ID as the username and your API Key as the password. Generated from the per-API specs at developers.envoke.com; do not edit by hand."
  },
  "servers": [
    {
      "url": "https://e1.envoke.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "basicAuth": []
    }
  ],
  "tags": [
    {
      "name": "Contacts",
      "description": "Operations on contacts."
    },
    {
      "name": "Tags",
      "description": "Operations on tags."
    },
    {
      "name": "Tag Contacts",
      "description": "Bulk add and remove contacts from a tag."
    },
    {
      "name": "Subscriptions",
      "description": "Operations on subscriptions."
    },
    {
      "name": "Subscription Contacts",
      "description": "Bulk add and remove contacts from a subscription."
    },
    {
      "name": "Custom Fields",
      "description": "Operations on contact-scoped custom fields."
    },
    {
      "name": "Groups",
      "description": "Operations on groups."
    },
    {
      "name": "Templates",
      "description": "Operations on email templates."
    },
    {
      "name": "Messages",
      "description": "Read, create, update, and delete messages."
    },
    {
      "name": "Sending",
      "description": "Send a draft, create and send in one request, and cancel a scheduled send."
    },
    {
      "name": "Send",
      "description": "Create a message and send it now, or send an existing Send API message to more recipients."
    },
    {
      "name": "Senders",
      "description": "Operations on sender identities."
    },
    {
      "name": "Message activity",
      "description": "Activity metrics per message (list)."
    },
    {
      "name": "Message stats",
      "description": "Full report for one message, and comparisons of several messages or a folder."
    },
    {
      "name": "Message contacts",
      "description": "The contacts behind one message's metrics (drill-down)."
    },
    {
      "name": "Contact activity",
      "description": "One contact's email activity, by id, email or remote ID."
    },
    {
      "name": "Hooks",
      "description": "Manage webhook subscriptions."
    }
  ],
  "x-tagGroups": [
    {
      "name": "Contacts",
      "tags": [
        "Contacts"
      ]
    },
    {
      "name": "Tags",
      "tags": [
        "Tags",
        "Tag Contacts"
      ]
    },
    {
      "name": "Subscriptions",
      "tags": [
        "Subscriptions",
        "Subscription Contacts"
      ]
    },
    {
      "name": "Custom fields",
      "tags": [
        "Custom Fields"
      ]
    },
    {
      "name": "Groups",
      "tags": [
        "Groups"
      ]
    },
    {
      "name": "Templates",
      "tags": [
        "Templates"
      ]
    },
    {
      "name": "Messages",
      "tags": [
        "Messages",
        "Sending"
      ]
    },
    {
      "name": "Send API",
      "tags": [
        "Send"
      ]
    },
    {
      "name": "Senders",
      "tags": [
        "Senders"
      ]
    },
    {
      "name": "Reports",
      "tags": [
        "Message activity",
        "Message stats",
        "Message contacts",
        "Contact activity"
      ]
    },
    {
      "name": "Hooks",
      "tags": [
        "Hooks"
      ]
    }
  ],
  "paths": {
    "/v1/contacts": {
      "get": {
        "summary": "List contacts",
        "description": "Returns a JSON array of contacts, or `[]` when none match. Supports filtering, sorting, pagination, and field selection. Without `sort`, the order is not defined. `limit=1` returns a bare object (or `[]`). Filter operators: equals and `(ne)` on every filterable field; `(regex)` on `custom_fields.{name}` only; `(gt)`, `(lt)`, `(gte)`, `(lte)` on `date_created`, `consent_effective_date`, and `consent_expiry` only; `(or)` and `(not)` groups. Each operator also has a `$` spelling (`$ne`, `$regex`, ...). `(in)` is not supported on any contacts field; use `(or)`. Separate filters are ANDed, and two operators on one field are ANDed. When the request has no `filter` parameter, every query parameter other than `limit`, `skip`, `sort`, `fields`, and `result_type` is treated as a filter, so `?email=alto.lead%40example.com` equals `?filter[email]=alto.lead%40example.com`, and a stray parameter is rejected (`Field 'key_field' is not valid for filtering`). Invalid parameters return 400 with every problem listed.",
        "operationId": "listContacts",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1ContactsLimit"
          },
          {
            "$ref": "#/components/parameters/V1ContactsSkip"
          },
          {
            "$ref": "#/components/parameters/V1ContactsFields"
          },
          {
            "$ref": "#/components/parameters/V1ContactsResultType"
          },
          {
            "name": "filter[{field}]",
            "in": "query",
            "description": "Profile fields: `id`, `external_id`, `remote_id`, `first_name`, `last_name`, `title`, `email`, `company`, `phone`, `address_1`, `address_2`, `city`, `country`, `province`, `postal_code`, `website`, `language`, `feedback`. Operators: equals and `(ne)`, e.g. `filter[city][(ne)]=Toronto`. `country` is matched against the stored 2-letter code (`CA`, not `Canada`). Another operator gives `'{SQL operator}' is not a valid operator for the field '{field}'` (e.g. `'REGEXP' ...` for `(regex)`, `'IN' ...` for `(in)`); an unknown field gives `Field '{field}' is not valid for filtering`; a list given to an operator (`filter[email][(ne)][]=...`) gives `Field '{field}' requires a scalar value`; a list with no operator (`filter[email][]=...`) gives `Field '0' is not valid for filtering`.",
            "schema": {
              "type": "string"
            },
            "example": "Toronto"
          },
          {
            "name": "filter[date_created]",
            "in": "query",
            "description": "Operators: equals, `(ne)`, `(gt)`, `(lt)`, `(gte)`, `(lte)`. Read as UTC unless the value has a time zone; a date with no time is read as midnight in Envoke's server time zone. An unreadable value gives `Invalid date format in filter condition for date_created`.",
            "schema": {
              "type": "string"
            },
            "example": "2026-01-01T00:00:00Z"
          },
          {
            "name": "filter[consent_status]",
            "in": "query",
            "description": "Consent type name. Operators: equals and `(ne)`.",
            "schema": {
              "type": "string"
            },
            "example": "Express"
          },
          {
            "name": "filter[consent_description]",
            "in": "query",
            "description": "Operators: equals and `(ne)`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[consent_effective_date]",
            "in": "query",
            "description": "Operators: equals, `(ne)`, `(gt)`, `(lt)`, `(gte)`, `(lte)`. Read as UTC unless the value has a time zone.",
            "schema": {
              "type": "string"
            },
            "example": "2026-09-01T00:00:00Z"
          },
          {
            "name": "filter[consent_expiry]",
            "in": "query",
            "description": "Operators: equals, `(ne)`, `(gt)`, `(lt)`, `(gte)`, `(lte)`. Compared as a plain `YYYY-MM-DD` date with no time zone conversion, against the stored expiry (GET blanking does not apply to filtering).",
            "schema": {
              "type": "string"
            },
            "example": "2027-03-31"
          },
          {
            "name": "filter[custom_fields.{name}]",
            "in": "query",
            "description": "A custom field, by its name exactly as defined (case-sensitive; names contain only letters, digits, and underscores), e.g. `filter[custom_fields.Voice_Part]=Alto`. Operators: equals, `(ne)`, and `(regex)` (a MySQL regular expression). `(ne)` matches only contacts that have a value for the field. Use the `filter[...]` form for custom fields: dots and spaces in a bare parameter name are not preserved. The bare name `filter[custom_fields]` is not a valid filter.",
            "schema": {
              "type": "string"
            },
            "example": "Alto"
          },
          {
            "name": "filter[interests]",
            "in": "query",
            "description": "Exact, case-sensitive name of a tag, subscription, or mandatory consent item; matches contacts that have it, and `(ne)` matches contacts that don't. IDs are not accepted. Operators: equals and `(ne)`. An unknown name gives `Invalid 'interest' provided: '{value}'`.",
            "schema": {
              "type": "string"
            },
            "example": "Volunteer"
          },
          {
            "name": "filter[autoresponders]",
            "in": "query",
            "description": "Exact name of a nurture campaign; works like `filter[interests]`. Operators: equals and `(ne)`. An unknown name gives `Invalid 'autoresponder' provided: '{value}'`.",
            "schema": {
              "type": "string"
            },
            "example": "Welcome Sequence"
          },
          {
            "name": "filter[(or)]",
            "in": "query",
            "description": "Matches if any of the numbered groups inside match: `filter[(or)][0][email]=a%40example.com&filter[(or)][1][email]=b%40example.com`. `$or` is the same.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[(not)]",
            "in": "query",
            "description": "Matches if the conditions inside do not all match: `filter[(not)][city]=Toronto&filter[(not)][language]=fr` excludes contacts in Toronto who are also French-speaking. `$not` is the same.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[{n}]",
            "in": "query",
            "description": "Numbered AND group, used to repeat a field: `filter[0][interests]=Volunteer&filter[1][interests]=Alumni` matches contacts with both tags.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort[{field}]",
            "in": "query",
            "description": "Sort direction `asc` or `desc` (case-insensitive), or `1` or `-1`. Repeat for a secondary sort. Sortable fields: `id`, `external_id`, `remote_id`, `first_name`, `last_name`, `title`, `email`, `company`, `phone`, `address_1`, `address_2`, `city`, `country`, `province`, `postal_code`, `website`, `language`, `feedback`, `date_created`, `consent_status`, `custom_fields.{name}`. Any other field (including `consent_description`, `consent_effective_date`, `consent_expiry`, `interests`, `autoresponders`) gives `Field '{field}' is not valid for sorting`; a bad direction gives `'{value}' is not a valid sort direction`; `sort` without a field (`?sort=email`) gives `Invalid 'sort': 'email'`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc",
                "ASC",
                "DESC",
                "1",
                "-1"
              ]
            },
            "example": "asc"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching contacts: an array, a key-value object with `result_type=kvp`, or a single object with `limit=1`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Contact"
                    },
                    {
                      "type": "object",
                      "description": "Contact ID to email address (`result_type=kvp`).",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "1001",
                        "external_id": "9f2c4e7a1b3d5f6a8c0e2b4d6f8a1c3e",
                        "date_created": "2026-10-05T14:03:11+00:00",
                        "remote_id": "",
                        "first_name": "Maya",
                        "last_name": "Okafor",
                        "title": "Section Lead",
                        "email": "alto.lead@example.com",
                        "company": "",
                        "phone": "416-555-0142",
                        "address_1": "48 Harbour Lane",
                        "address_2": "Unit 5",
                        "city": "Toronto",
                        "country": "CA",
                        "province": "ON",
                        "postal_code": "M5V 0Z9",
                        "website": "",
                        "language": "en",
                        "feedback": "",
                        "consent_status": "Express",
                        "consent_description": "Signed up at the spring concert",
                        "consent_effective_date": "2026-09-12T18:30:00+00:00",
                        "consent_expiry": "",
                        "custom_fields": {
                          "Voice_Part": "Alto",
                          "Season_Ticket_Number": "HCS-ST-0417"
                        },
                        "interests": [
                          "Volunteer",
                          "Alumni",
                          "Concert News"
                        ],
                        "autoresponders": []
                      }
                    ]
                  },
                  "fields": {
                    "summary": "Selected fields (fields=id,email,consent_status)",
                    "value": [
                      {
                        "id": "1001",
                        "email": "alto.lead@example.com",
                        "consent_status": "Express"
                      },
                      {
                        "id": "1002",
                        "email": "tenor.remote@example.com",
                        "consent_status": "Express"
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value response (result_type=kvp)",
                    "value": {
                      "1001": "alto.lead@example.com",
                      "1002": "tenor.remote@example.com"
                    }
                  },
                  "single": {
                    "summary": "limit=1 returns an object (?email=alto.lead%40example.com&fields=id&limit=1)",
                    "value": {
                      "id": "1001"
                    }
                  },
                  "empty": {
                    "summary": "No contacts match",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid list parameters. Every problem is listed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ContactsValidationErrorResponse"
                },
                "examples": {
                  "filter_errors": {
                    "summary": "Unknown field and unsupported operator",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Field 'nickname' is not valid for filtering",
                          "'REGEXP' is not a valid operator for the field 'email'"
                        ]
                      }
                    }
                  },
                  "paging_errors": {
                    "summary": "Bad limit and skip",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'limit': 'ten'",
                          "Invalid 'skip': 'abc'"
                        ]
                      }
                    }
                  },
                  "sort_error": {
                    "summary": "Field cannot be sorted",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Field 'interests' is not valid for sorting"
                        ]
                      }
                    }
                  },
                  "unknown_interest": {
                    "summary": "Unknown tag name in an interests filter",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'interest' provided: 'Volunteers'"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      },
      "post": {
        "summary": "Create a contact",
        "description": "Creates a contact from a single JSON object. `email` is required. The existence check uses `key_field` (`email` by default, or `remote_id`); with `upsert`, an existing match is updated instead of rejected. The email, `key_field`, and duplicate checks run first and return on their own; the remaining body errors are then collected and returned together; field lengths, email format, and the account's duplicate check run last and use the field-validation envelope. Unrecognised `country` and `language` values are warnings appended to `result_text`. Custom field values that cannot be stored and nurture campaigns skipped for lack of consent are not reported.",
        "operationId": "createContact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyFieldCreate"
          },
          {
            "$ref": "#/components/parameters/Upsert"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactCreate"
              },
              "examples": {
                "minimal": {
                  "summary": "Only an email address (consent becomes Not Provided)",
                  "value": {
                    "email": "to.delete@example.com"
                  }
                },
                "full": {
                  "summary": "Full contact with express consent",
                  "value": {
                    "email": "alto.lead@example.com",
                    "first_name": "Maya",
                    "last_name": "Okafor",
                    "title": "Section Lead",
                    "phone": "416-555-0142",
                    "address_1": "48 Harbour Lane",
                    "address_2": "Unit 5",
                    "city": "Toronto",
                    "province": "ON",
                    "postal_code": "M5V 0Z9",
                    "country": "Canada",
                    "language": "English",
                    "consent_status": "Express",
                    "consent_description": "Signed up at the spring concert",
                    "consent_effective_date": "2026-09-12T18:30:00Z"
                  }
                },
                "custom_fields_and_interests": {
                  "summary": "Custom fields, tags, a subscription, and a nurture campaign",
                  "value": {
                    "email": "alto.lead@example.com",
                    "consent_status": "Express",
                    "custom_fields": {
                      "Voice_Part": "Alto",
                      "Season_Ticket_Number": "HCS-ST-0417"
                    },
                    "interests": [
                      "Volunteer",
                      "Alumni",
                      "Concert News"
                    ],
                    "autoresponders": [
                      "Welcome Sequence"
                    ]
                  }
                },
                "remote_id": {
                  "summary": "Keyed on your own ID (?key_field=remote_id)",
                  "value": {
                    "email": "tenor.remote@example.com",
                    "remote_id": "HCS-0002",
                    "first_name": "Daniel",
                    "last_name": "Reyes"
                  }
                },
                "eu_implied": {
                  "summary": "EU contact with implied consent",
                  "value": {
                    "email": "paris.member@example.org",
                    "first_name": "Camille",
                    "last_name": "Laurent",
                    "country": "France",
                    "consent_status": "Implied - Inquiry"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact created, or updated by an upsert. `result_data` is the full contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ContactsCreateSuccessResponse"
                },
                "examples": {
                  "created": {
                    "summary": "Contact created (result_data shortened)",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully created.",
                      "result_data": {
                        "id": "1005",
                        "external_id": "4b8d2f6a0c1e3a5b7d9f1e3c5a7b9d0f",
                        "date_created": "2026-10-05T15:20:44+00:00",
                        "first_name": "Priya",
                        "last_name": "Nair",
                        "email": "upsert.target@example.com",
                        "consent_status": "Not Provided",
                        "consent_effective_date": "2026-10-05T15:20:44+00:00",
                        "consent_expiry": "",
                        "custom_fields": {},
                        "interests": [],
                        "autoresponders": []
                      }
                    }
                  },
                  "created_with_warnings": {
                    "summary": "Unrecognised country and language (result_data omitted)",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully created. 'Narnia' is not a valid value for country; field will not be updated. 'de' is not a valid value for language; field will not be updated",
                      "result_data": {
                        "id": "1006",
                        "email": "bulk.a@example.com",
                        "country": "",
                        "language": ""
                      }
                    }
                  },
                  "upsert_updated": {
                    "summary": "Upsert matched an existing contact (result_data shortened)",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated.",
                      "result_data": {
                        "id": "1005",
                        "email": "upsert.target@example.com",
                        "company": "Harbourfront Choral Society",
                        "consent_status": "Express"
                      }
                    }
                  },
                  "eu_implied": {
                    "summary": "Implied consent does not apply in the EU (result_data shortened)",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully created. Their consent status was set to Not Provided because Implied - Inquiry does not apply to contacts in the EU.",
                      "result_data": {
                        "id": "1003",
                        "email": "paris.member@example.org",
                        "country": "FR",
                        "consent_status": "Not Provided",
                        "consent_description": "Implied - Inquiry consent does not apply to contacts in the EU.",
                        "consent_expiry": ""
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, field-validation error, or unreadable body.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1ContactsValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FieldValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V1ContactsPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "email_required": {
                    "summary": "Missing email",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email is required"
                        ]
                      }
                    }
                  },
                  "duplicate": {
                    "summary": "Email already exists and upsert is not set",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "email value not unique ('alto.lead@example.com' duplicates existing contact with id: '1001')"
                        ]
                      }
                    }
                  },
                  "multiple_matches": {
                    "summary": "More than one contact has the key value (returned even with upsert)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Multiple contacts already exist with 'remote_id' value 'HCS-0002'.  (IDs: 1002, 1010)"
                        ]
                      }
                    }
                  },
                  "body_errors": {
                    "summary": "Body errors collected together",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'nickname' is not a valid contact field",
                          "Invalid 'consent_status': 'Opted In' (possible values: 'Express', 'Implied - Inquiry', 'Implied - Transaction', 'Implied - No Expiry', 'Not Provided', 'Revoked', 'Envoke Spam Reported')"
                        ]
                      }
                    }
                  },
                  "field_validation": {
                    "summary": "Field lengths and email format",
                    "value": {
                      "success": false,
                      "result_text": "Email invalid, First name must not exceed 50 characters",
                      "result_data": {
                        "fields": {
                          "email": "Email invalid",
                          "fname": "First name must not exceed 50 characters"
                        }
                      }
                    }
                  },
                  "duplicate_check": {
                    "summary": "Account duplicate check with key_field=remote_id",
                    "value": {
                      "success": false,
                      "result_text": "contact with email = alto.lead@example.com already exists",
                      "result_data": {
                        "fields": []
                      }
                    }
                  },
                  "invalid_request": {
                    "summary": "Missing, empty, or {} body",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid request"
                    }
                  },
                  "invalid_json": {
                    "summary": "Body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      },
      "patch": {
        "summary": "Update one contact by key field, or several contacts",
        "description": "With an object body, finds one contact by `key_field` and returns the full, updated contact; warnings are appended to `result_text` after `. `, giving a double period. Without `key_field`, a body `id` is used directly (not looked up) and otherwise `email` is used, so a body with `id` and a new `email` changes that contact's email. Lookup failures use the plain envelope. With a list body, each object is matched by its `id` (used directly) or else by `email`; `key_field` is ignored, so list items cannot be matched by `remote_id`. Each found contact is updated exactly as by `PATCH /contacts/{id}`. If any contact fails, the response is HTTP 400 with `success: false`, even though the others were updated.",
        "operationId": "updateContacts",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/KeyFieldUpdate"
          },
          {
            "$ref": "#/components/parameters/WriteEmptyValues"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/ContactUpdate"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ContactUpdate"
                    }
                  }
                ]
              },
              "examples": {
                "by_email": {
                  "summary": "One contact found by email",
                  "value": {
                    "email": "alto.lead@example.com",
                    "company": "Harbourfront Choral Society"
                  }
                },
                "by_remote_id": {
                  "summary": "One contact found by remote_id (?key_field=remote_id)",
                  "value": {
                    "remote_id": "HCS-0002",
                    "first_name": "Dan"
                  }
                },
                "bulk": {
                  "summary": "Several contacts",
                  "value": [
                    {
                      "email": "bulk.a@example.com",
                      "city": "Hamilton"
                    },
                    {
                      "email": "bulk.b@example.com",
                      "city": "Hamilton"
                    },
                    {
                      "email": "missing.member@example.com",
                      "city": "Hamilton"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Object body: the full contact. List body: every contact was updated.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/UpdateByKeySuccessResponse"
                    },
                    {
                      "$ref": "#/components/schemas/BulkUpdateResponse"
                    }
                  ]
                },
                "examples": {
                  "by_key": {
                    "summary": "Object body, with a warning (result_data shortened)",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated.. 'Narnia' is not a valid value for country; field will not be updated",
                      "result_data": {
                        "id": "1002",
                        "email": "tenor.remote@example.com",
                        "remote_id": "HCS-0002",
                        "first_name": "Dan",
                        "country": ""
                      }
                    }
                  },
                  "bulk_all_succeeded": {
                    "summary": "List body, every contact updated",
                    "value": {
                      "success": true,
                      "successes": [
                        "1006",
                        "1007"
                      ],
                      "failures": [],
                      "result_data": {
                        "1006": {
                          "id": 1006,
                          "submit_type": "update",
                          "warnings": []
                        },
                        "1007": {
                          "id": 1007,
                          "submit_type": "update",
                          "warnings": []
                        }
                      },
                      "result_text": "All contacts were successfully updated"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Lookup failure, validation failure, unreadable body, or a list body in which any contact failed.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1ContactsPlainErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V1ContactsValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FieldValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/BulkUpdateResponse"
                    }
                  ]
                },
                "examples": {
                  "not_found_by_key": {
                    "summary": "No contact has the key value",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "No contact was found with the 'email': 'nobody@example.com'"
                    }
                  },
                  "multiple_by_key": {
                    "summary": "More than one contact has the key value",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Multiple contacts were found with the 'remote_id': 'HCS-0002'.  (IDs: 1002, 1010)"
                    }
                  },
                  "key_field_missing": {
                    "summary": "key_field set but not in the body",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Specified key_field 'remote_id' field was not provided."
                    }
                  },
                  "no_identifier": {
                    "summary": "No key_field, and neither id nor email in the body",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "An 'id' or 'email' (or alternate field specified by key_field query parameter) must be provided in order to update a contact"
                    }
                  },
                  "invalid_key_field": {
                    "summary": "key_field is not id, email, or remote_id",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid 'key_field' value: 'external_id'"
                    }
                  },
                  "key_field_id_mismatch": {
                    "summary": "Body id differs from the contact the key field found",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "The specified contact ID 1002 does not match record identified by key_field 'email': 'alto.lead@example.com' (ID: 1001)"
                    }
                  },
                  "body_id_not_found": {
                    "summary": "Body id without key_field does not exist",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No contact was found with ID: '999999'"
                        ]
                      }
                    }
                  },
                  "bulk_partial_failure": {
                    "summary": "List body, one contact not found",
                    "value": {
                      "success": false,
                      "successes": [
                        "1006",
                        "1007"
                      ],
                      "failures": [
                        "CONTACT_OBJECT_2"
                      ],
                      "result_data": {
                        "1006": {
                          "id": 1006,
                          "submit_type": "update",
                          "warnings": []
                        },
                        "1007": {
                          "id": 1007,
                          "submit_type": "update",
                          "warnings": []
                        },
                        "CONTACT_OBJECT_2": {
                          "errors": [
                            "No contact was found with the 'email': 'missing.member@example.com'"
                          ]
                        }
                      },
                      "result_text": "2 contacts were successfully updated; 1 contacts were not updated"
                    }
                  },
                  "invalid_request": {
                    "summary": "Missing, empty, {}, or [] body",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      }
    },
    "/v1/contacts/{id}": {
      "get": {
        "summary": "Get a single contact",
        "description": "Returns one contact as an object, in the same shape as an item of the list response. `fields` selects fields; filter, sort, and paging parameters are ignored. Returns an empty array (`[]`) if the ID is not found.",
        "operationId": "getContact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1ContactsContactId"
          },
          {
            "$ref": "#/components/parameters/V1ContactsFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact, or `[]` if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Contact"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Contact found",
                    "value": {
                      "id": "1001",
                      "external_id": "9f2c4e7a1b3d5f6a8c0e2b4d6f8a1c3e",
                      "date_created": "2026-10-05T14:03:11+00:00",
                      "remote_id": "",
                      "first_name": "Maya",
                      "last_name": "Okafor",
                      "title": "Section Lead",
                      "email": "alto.lead@example.com",
                      "company": "",
                      "phone": "416-555-0142",
                      "address_1": "48 Harbour Lane",
                      "address_2": "Unit 5",
                      "city": "Toronto",
                      "country": "CA",
                      "province": "ON",
                      "postal_code": "M5V 0Z9",
                      "website": "",
                      "language": "en",
                      "feedback": "",
                      "consent_status": "Express",
                      "consent_description": "Signed up at the spring concert",
                      "consent_effective_date": "2026-09-12T18:30:00+00:00",
                      "consent_expiry": "",
                      "custom_fields": {
                        "Voice_Part": "Alto",
                        "Season_Ticket_Number": "HCS-ST-0417"
                      },
                      "interests": [
                        "Volunteer",
                        "Alumni",
                        "Concert News"
                      ],
                      "autoresponders": []
                    }
                  },
                  "not_found": {
                    "summary": "Contact not found",
                    "value": []
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      },
      "patch": {
        "summary": "Update a contact by ID",
        "description": "Updates the contact with the given ID and returns a short result: `result_data.id` (an integer here), `submit_type`, and `warnings` for unrecognised `country` or `language` values, plus `no_affected_rows: true` when no profile column changed, and `inapplicable_consent_updated` and `consent_fix_message` when an EU country change reset the contact's consent. `key_field` is ignored. If the body includes `id`, it must equal the URL id. A list body is treated as a bulk update (see `PATCH /contacts`) and the URL id is ignored. Preempted or blocked consent changes and skipped nurture campaigns are not reported.",
        "operationId": "updateContact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1ContactsContactId"
          },
          {
            "$ref": "#/components/parameters/WriteEmptyValues"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/ContactUpdate"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ContactUpdate"
                    }
                  }
                ]
              },
              "examples": {
                "profile": {
                  "summary": "Change profile fields",
                  "value": {
                    "title": "Alto Section Lead",
                    "phone": "416-555-0199"
                  }
                },
                "clear_value": {
                  "summary": "Clear a value (?write_empty_values=1)",
                  "value": {
                    "address_2": ""
                  }
                },
                "clear_custom_field": {
                  "summary": "Clear a custom field (no write_empty_values needed)",
                  "value": {
                    "custom_fields": {
                      "Season_Ticket_Number": ""
                    }
                  }
                },
                "remove_tag": {
                  "summary": "Remove a tag, leaving others",
                  "value": {
                    "interests": {
                      "Alumni": "Unset"
                    }
                  }
                },
                "dequeue": {
                  "summary": "Stop a nurture campaign",
                  "value": {
                    "autoresponders": {
                      "Welcome Sequence": "Dequeue"
                    }
                  }
                },
                "force_downgrade": {
                  "summary": "Force a consent downgrade",
                  "value": {
                    "consent_status": "Implied - Transaction",
                    "consent_description": "Ticket purchase only; express consent withdrawn by phone",
                    "consent_force_downgrade": true
                  }
                },
                "eu_country": {
                  "summary": "Change country to an EU country",
                  "value": {
                    "country": "Germany"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact updated (object body), or every contact updated (list body).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/UpdateByIdSuccessResponse"
                    },
                    {
                      "$ref": "#/components/schemas/BulkUpdateResponse"
                    }
                  ]
                },
                "examples": {
                  "updated": {
                    "summary": "Contact updated",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated.",
                      "result_data": {
                        "id": 1001,
                        "submit_type": "update",
                        "warnings": []
                      }
                    }
                  },
                  "with_warning": {
                    "summary": "Unrecognised country",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated.",
                      "result_data": {
                        "id": 1001,
                        "submit_type": "update",
                        "warnings": [
                          "'Narnia' is not a valid value for country; field will not be updated"
                        ]
                      }
                    }
                  },
                  "no_affected_rows": {
                    "summary": "Only a custom field changed",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated.",
                      "result_data": {
                        "id": 1001,
                        "submit_type": "update",
                        "warnings": [],
                        "no_affected_rows": true
                      }
                    }
                  },
                  "eu_country_change": {
                    "summary": "Country changed to an EU country; implied consent reset",
                    "value": {
                      "success": true,
                      "result_text": "The contact was successfully updated. Their consent status was updated because Implied - Inquiry consent does not apply to contacts in the EU.",
                      "result_data": {
                        "id": 1004,
                        "submit_type": "update",
                        "inapplicable_consent_updated": true,
                        "consent_fix_message": " Their consent status was updated because Implied - Inquiry consent does not apply to contacts in the EU.",
                        "warnings": []
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not found, id mismatch, validation failure, field-validation failure, or unreadable body.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1ContactsValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FieldValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V1ContactsPlainErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/BulkUpdateResponse"
                    }
                  ]
                },
                "examples": {
                  "not_found": {
                    "summary": "No contact has this ID",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No contact was found with ID: '999999'"
                        ]
                      }
                    }
                  },
                  "unknown_field": {
                    "summary": "Body has a key that is not a contact field",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'nickname' is not a valid contact field"
                        ]
                      }
                    }
                  },
                  "field_validation": {
                    "summary": "Field too long (update envelope carries warnings)",
                    "value": {
                      "success": false,
                      "result_data": {
                        "fields": {
                          "fname": "First name must not exceed 50 characters"
                        },
                        "warnings": []
                      },
                      "result_text": "First name must not exceed 50 characters"
                    }
                  },
                  "email_empty": {
                    "summary": "Email cleared with write_empty_values=1",
                    "value": {
                      "success": false,
                      "result_data": {
                        "fields": {
                          "email": "Email must not be empty"
                        },
                        "warnings": []
                      },
                      "result_text": "Email must not be empty"
                    }
                  },
                  "id_mismatch": {
                    "summary": "Body id differs from the URL id",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Contact 'id': 1001 does not match 'id' in request body (1002)"
                    }
                  },
                  "invalid_json": {
                    "summary": "Body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      },
      "delete": {
        "summary": "Delete a contact",
        "description": "Permanently deletes the contact: its record, consent history, custom field values, tag, subscription, and mandatory consent memberships, nurture campaign memberships and queued campaign actions, and form submissions. The ID is never reused. A contact whose consent status is `Revoked`, `Envoke Spam Reported`, or `External Spam Reported` cannot be deleted. Responses have only `success` and `result_text`. If the delete fails on the server, `result_text` is `1 contact not deleted, 1 - delete error: ` followed by the error detail.",
        "operationId": "deleteContact",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1ContactsContactId"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ContactsDeleteResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "1 contact deleted"
                }
              }
            }
          },
          "400": {
            "description": "Not found, blocked by consent status, or a server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1ContactsDeleteResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "No contact has this ID",
                    "value": {
                      "success": false,
                      "result_text": "1 contact not deleted, 1 - no matching contact found"
                    }
                  },
                  "blocked": {
                    "summary": "Consent status blocks deletion",
                    "value": {
                      "success": false,
                      "result_text": "1 contact not deleted, 1 - can't delete due to consent status"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1ContactsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1ContactsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1ContactsTooManyRequests"
          }
        }
      }
    },
    "/v2/tags": {
      "get": {
        "summary": "List tags",
        "description": "Returns a paginated array of tags for the authenticated account, sorted alphabetically by name by default. Supports filtering by `id`, `name`, `external_id`, `group`, and `description`. Use `?result_type=kvp` for a flat id-to-name map. Filter operators supported: exact match and `(ne)` (not equal).",
        "operationId": "listTags",
        "tags": [
          "Tags"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2TagsLimit"
          },
          {
            "$ref": "#/components/parameters/V2TagsSkip"
          },
          {
            "$ref": "#/components/parameters/V2TagsFields"
          },
          {
            "$ref": "#/components/parameters/V2TagsResultType"
          },
          {
            "name": "filter[id]",
            "in": "query",
            "description": "Filter by tag ID. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[name]",
            "in": "query",
            "description": "Filter by tag name. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[external_id]",
            "in": "query",
            "description": "Filter by external ID. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[group]",
            "in": "query",
            "description": "Filter by group name. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[description]",
            "in": "query",
            "description": "Filter by description. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort[name]",
            "in": "query",
            "description": "Sort by name. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of tags.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Tag"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of tag ID to name.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "42",
                        "external_id": "a1b2c3d4e5f6",
                        "name": "Newsletter Subscribers",
                        "group": "Engagement",
                        "description": "Contacts who opted in to the monthly newsletter",
                        "display_names": {
                          "en": "Newsletter"
                        }
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "42": "Newsletter Subscribers",
                      "43": "Event Attendees"
                    }
                  },
                  "empty": {
                    "summary": "No tags",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a tag",
        "description": "Creates a new tag. `name` is required. Optional fields: `group`, `description`, `display_names`.",
        "operationId": "createTag",
        "tags": [
          "Tags"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagCreate"
              },
              "examples": {
                "basic": {
                  "summary": "Create a basic tag",
                  "value": {
                    "name": "Newsletter Subscribers"
                  }
                },
                "with_group": {
                  "summary": "Create with group and description",
                  "value": {
                    "name": "Annual Conference Attendees",
                    "group": "Events",
                    "description": "Members who attend our annual conference"
                  }
                },
                "with_display_names": {
                  "summary": "Create with translated display names",
                  "value": {
                    "name": "Newsletter Subscribers",
                    "display_names": {
                      "en": "Newsletter",
                      "fr": "Infolettre"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "42",
                    "external_id": "a1b2c3d4e5f6",
                    "name": "Newsletter Subscribers",
                    "group": "Engagement",
                    "description": "Contacts who opted in to the monthly newsletter",
                    "display_names": {
                      "en": "Newsletter"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
                },
                "examples": {
                  "name_required": {
                    "summary": "Missing name",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' is required"
                        ]
                      }
                    }
                  },
                  "invalid_group": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Nonexistent Group'"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/tags/{id}": {
      "get": {
        "summary": "Get a single tag",
        "description": "Returns a single tag by its ID. Returns an empty array (`[]`) if the ID is not found.",
        "operationId": "getTag",
        "tags": [
          "Tags"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TagId"
          },
          {
            "$ref": "#/components/parameters/V2TagsFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested tag, or an empty array if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Tag"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Tag found",
                    "value": {
                      "id": "42",
                      "external_id": "a1b2c3d4e5f6",
                      "name": "Newsletter Subscribers",
                      "group": "Engagement",
                      "description": "Contacts who opted in to the monthly newsletter",
                      "display_names": {
                        "en": "Newsletter"
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Tag not found",
                    "value": []
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a tag",
        "description": "Updates one or more fields on an existing tag. `name`, `group`, `description`, and `display_names` are updatable. `external_id` is silently ignored if included. If `id` is included in the body, it must match the `{id}` in the URL — if they differ, a validation envelope is returned.",
        "operationId": "updateTag",
        "tags": [
          "Tags"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TagId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagUpdate"
              },
              "examples": {
                "rename": {
                  "summary": "Rename the tag",
                  "value": {
                    "name": "Updated Tag Name"
                  }
                },
                "assign_group": {
                  "summary": "Assign to a group",
                  "value": {
                    "group": "Certification Programs"
                  }
                },
                "remove_group": {
                  "summary": "Remove group assignment",
                  "value": {
                    "group": ""
                  }
                },
                "update_display_names": {
                  "summary": "Update display names",
                  "value": {
                    "display_names": {
                      "en": "Organic",
                      "fr": "Biologique"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "42",
                    "external_id": "a1b2c3d4e5f6",
                    "name": "Updated Tag Name",
                    "group": "Certification Programs",
                    "description": "Updated description",
                    "display_names": {
                      "en": "Updated Tag"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, tag not found, or id mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "Tag not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  },
                  "invalid_group": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Does Not Exist'"
                        ]
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "id in body does not match URL id",
                    "value": {
                      "success": false,
                      "result_text": "Provided 'id': 42 does not match 'id' in request body (999)",
                      "result_data": []
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a tag",
        "description": "Permanently deletes a tag and all contact-to-tag associations. Contacts are not deleted — they simply lose their assignment to this tag.",
        "operationId": "deleteTag",
        "tags": [
          "Tags"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TagId"
          }
        ],
        "responses": {
          "200": {
            "description": "Tag deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsDeleteSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Deleted",
                  "result_data": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/V2TagsNotFoundMutate"
          }
        }
      }
    },
    "/v2/tags/{id}/contacts": {
      "post": {
        "summary": "Add contacts to a tag",
        "description": "Bulk-assigns contacts to a tag. Accepts either `contact_ids` or `contact_emails` (not both). Maximum 1,000 items per request. The operation is idempotent — adding a contact that is already tagged is a no-op.",
        "operationId": "addTagContacts",
        "tags": [
          "Tag Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TagId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactsRequest"
              },
              "examples": {
                "by_ids": {
                  "summary": "Add by contact IDs",
                  "value": {
                    "contact_ids": [
                      101,
                      102,
                      103
                    ]
                  }
                },
                "by_emails": {
                  "summary": "Add by email addresses",
                  "value": {
                    "contact_emails": [
                      "alice@example.com",
                      "bob@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed. Check `success` and `result_data` for details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactsSuccessResponse"
                },
                "examples": {
                  "all_found": {
                    "summary": "All contacts found and updated",
                    "value": {
                      "success": true,
                      "result_text": "Set to contacts",
                      "result_data": {
                        "processed": 3
                      }
                    }
                  },
                  "some_not_found": {
                    "summary": "Some contacts not found",
                    "value": {
                      "success": true,
                      "result_text": "Set to contacts. Some contacts not found",
                      "result_data": {
                        "processed": 3,
                        "not_found": [
                          "ghost@example.com"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or tag not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
                },
                "examples": {
                  "no_field": {
                    "summary": "Neither contact_ids nor contact_emails provided",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Either 'contact_ids' or 'contact_emails' must be provided"
                        ]
                      }
                    }
                  },
                  "both_fields": {
                    "summary": "Both contact_ids and contact_emails provided",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Provide either 'contact_ids' or 'contact_emails', not both"
                        ]
                      }
                    }
                  },
                  "tag_not_found": {
                    "summary": "Tag not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove contacts from a tag",
        "description": "Bulk-removes contacts from a tag. Accepts either `contact_ids` or `contact_emails` (not both). Maximum 1,000 items per request. The operation is idempotent — removing a contact that is not tagged is a no-op.",
        "operationId": "removeTagContacts",
        "tags": [
          "Tag Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TagId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactsRequest"
              },
              "examples": {
                "by_ids": {
                  "summary": "Remove by contact IDs",
                  "value": {
                    "contact_ids": [
                      101,
                      102
                    ]
                  }
                },
                "by_emails": {
                  "summary": "Remove by email addresses",
                  "value": {
                    "contact_emails": [
                      "alice@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed. Check `success` and `result_data` for details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactsSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Set to contacts",
                  "result_data": {
                    "processed": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or tag not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/subscriptions": {
      "get": {
        "summary": "List subscriptions",
        "description": "Returns a paginated array of subscriptions for the authenticated account, sorted alphabetically by name by default. Supports filtering by `id`, `name`, `external_id`, `group`, and `prefpage_access`. Use `?result_type=kvp` for a flat id-to-name map. Filter operators supported: exact match and `(ne)` (not equal).",
        "operationId": "listSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2SubscriptionsLimit"
          },
          {
            "$ref": "#/components/parameters/V2SubscriptionsSkip"
          },
          {
            "$ref": "#/components/parameters/V2SubscriptionsFields"
          },
          {
            "$ref": "#/components/parameters/V2SubscriptionsResultType"
          },
          {
            "name": "filter[id]",
            "in": "query",
            "description": "Filter by subscription ID. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[name]",
            "in": "query",
            "description": "Filter by subscription name. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[external_id]",
            "in": "query",
            "description": "Filter by external ID. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[group]",
            "in": "query",
            "description": "Filter by group name. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[prefpage_access]",
            "in": "query",
            "description": "Filter by preference page access. Supports `(ne)` operator.",
            "schema": {
              "type": "string",
              "enum": [
                "public",
                "private"
              ]
            }
          },
          {
            "name": "sort[name]",
            "in": "query",
            "description": "Sort by name. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of subscription ID to name.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "12",
                        "external_id": "a1b2c3d4e5f6",
                        "name": "Campus Weekly Newsletter",
                        "group": "Student Communications",
                        "prefpage_access": "public",
                        "display_names": {
                          "en": "Campus Weekly Newsletter",
                          "fr": "Bulletin hebdomadaire du campus"
                        },
                        "display_descriptions": {
                          "en": "A weekly roundup of campus news and events.",
                          "fr": ""
                        }
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "12": "Campus Weekly Newsletter",
                      "13": "Alumni Quarterly"
                    }
                  },
                  "empty": {
                    "summary": "No subscriptions",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a subscription",
        "description": "Creates a new subscription. `name` is required. Optional fields: `group`, `prefpage_access`, `display_names`, `display_descriptions`.",
        "operationId": "createSubscription",
        "tags": [
          "Subscriptions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionCreate"
              },
              "examples": {
                "basic": {
                  "summary": "Create a basic subscription",
                  "value": {
                    "name": "Campus Weekly Newsletter"
                  }
                },
                "with_group": {
                  "summary": "Create with group",
                  "value": {
                    "name": "Alumni Quarterly",
                    "group": "Alumni Relations"
                  }
                },
                "with_translations": {
                  "summary": "Create with display names and descriptions",
                  "value": {
                    "name": "Campus Weekly Newsletter",
                    "group": "Student Communications",
                    "display_names": {
                      "en": "Campus Weekly Newsletter",
                      "fr": "Bulletin hebdomadaire du campus"
                    },
                    "display_descriptions": {
                      "en": "A weekly roundup of campus news and events."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "12",
                    "external_id": "a1b2c3d4e5f6",
                    "name": "Campus Weekly Newsletter",
                    "group": "Student Communications",
                    "prefpage_access": "public",
                    "display_names": {
                      "en": "Campus Weekly Newsletter",
                      "fr": ""
                    },
                    "display_descriptions": {
                      "en": "A weekly roundup of campus news and events.",
                      "fr": ""
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                },
                "examples": {
                  "name_required": {
                    "summary": "Missing name",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' is required"
                        ]
                      }
                    }
                  },
                  "invalid_group": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Nonexistent Group'"
                        ]
                      }
                    }
                  },
                  "invalid_prefpage_access": {
                    "summary": "Invalid prefpage_access value",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'prefpage_access' must be 'public' or 'private'"
                        ]
                      }
                    }
                  },
                  "prefpage_access_feature_required": {
                    "summary": "Private subscriptions feature not enabled",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Setting 'prefpage_access' to 'private' requires the private subscriptions feature"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/subscriptions/{id}": {
      "get": {
        "summary": "Get a single subscription",
        "description": "Returns a single subscription by its ID. Returns an empty array (`[]`) if the ID is not found.",
        "operationId": "getSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          },
          {
            "$ref": "#/components/parameters/V2SubscriptionsFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested subscription, or an empty array if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Subscription"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Subscription found",
                    "value": {
                      "id": "12",
                      "external_id": "a1b2c3d4e5f6",
                      "name": "Campus Weekly Newsletter",
                      "group": "Student Communications",
                      "prefpage_access": "public",
                      "display_names": {
                        "en": "Campus Weekly Newsletter",
                        "fr": "Bulletin hebdomadaire du campus"
                      },
                      "display_descriptions": {
                        "en": "A weekly roundup of campus news and events.",
                        "fr": ""
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Subscription not found",
                    "value": []
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a subscription",
        "description": "Updates one or more fields on an existing subscription. `name`, `group`, `prefpage_access`, `display_names`, and `display_descriptions` are updatable. `external_id` is silently ignored if included. If `id` is included in the body, it must match the `{id}` in the URL — if they differ, a validation envelope is returned.",
        "operationId": "updateSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionUpdate"
              },
              "examples": {
                "rename": {
                  "summary": "Rename the subscription",
                  "value": {
                    "name": "Campus Weekly (Updated)"
                  }
                },
                "assign_group": {
                  "summary": "Assign to a group",
                  "value": {
                    "group": "Student Communications"
                  }
                },
                "remove_group": {
                  "summary": "Remove group assignment",
                  "value": {
                    "group": ""
                  }
                },
                "update_display_names": {
                  "summary": "Update display names",
                  "value": {
                    "display_names": {
                      "en": "Campus Weekly",
                      "fr": "Campus hebdo"
                    }
                  }
                },
                "update_display_descriptions": {
                  "summary": "Update display descriptions",
                  "value": {
                    "display_descriptions": {
                      "en": "Updated description for the weekly newsletter."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "12",
                    "external_id": "a1b2c3d4e5f6",
                    "name": "Campus Weekly (Updated)",
                    "group": "Student Communications",
                    "prefpage_access": "public",
                    "display_names": {
                      "en": "Campus Weekly",
                      "fr": "Campus hebdo"
                    },
                    "display_descriptions": {
                      "en": "Updated description for the weekly newsletter.",
                      "fr": ""
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, subscription not found, or id mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "Subscription not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  },
                  "invalid_group": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Does Not Exist'"
                        ]
                      }
                    }
                  },
                  "prefpage_access_feature_required": {
                    "summary": "Private subscriptions feature not enabled",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Setting 'prefpage_access' to 'private' requires the private subscriptions feature"
                        ]
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "id in body does not match URL id",
                    "value": {
                      "success": false,
                      "result_text": "Provided 'id': 12 does not match 'id' in request body (999)",
                      "result_data": []
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a subscription",
        "description": "Permanently deletes a subscription. **The subscription must have no contacts assigned to it** — attempting to delete one with contacts assigned returns a validation error. Remove all contacts first using the `DELETE /subscriptions/{id}/contacts` endpoint.",
        "operationId": "deleteSubscription",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription deleted successfully, or a validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2SubscriptionsDeleteSuccessResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "success": {
                    "summary": "Deleted successfully",
                    "value": {
                      "success": true,
                      "result_text": "Deleted",
                      "result_data": []
                    }
                  },
                  "not_found": {
                    "summary": "Subscription not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  },
                  "has_contacts": {
                    "summary": "Cannot delete — contacts assigned",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Cannot delete a subscription that has contacts assigned to it"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/subscriptions/{id}/contacts": {
      "post": {
        "summary": "Add contacts to a subscription",
        "description": "Bulk-assigns contacts to a subscription. Accepts either `contact_ids` or `contact_emails` (not both). Maximum 1,000 items per request. The operation is idempotent — adding a contact that is already subscribed is a no-op.",
        "operationId": "addSubscriptionContacts",
        "tags": [
          "Subscription Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactsRequest"
              },
              "examples": {
                "by_ids": {
                  "summary": "Add by contact IDs",
                  "value": {
                    "contact_ids": [
                      101,
                      102,
                      103
                    ]
                  }
                },
                "by_emails": {
                  "summary": "Add by email addresses",
                  "value": {
                    "contact_emails": [
                      "alice@example.com",
                      "bob@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed. Check `success` and `result_data` for details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactsSuccessResponse"
                },
                "examples": {
                  "all_found": {
                    "summary": "All contacts found and updated",
                    "value": {
                      "success": true,
                      "result_text": "Set to contacts",
                      "result_data": {
                        "processed": 3
                      }
                    }
                  },
                  "some_not_found": {
                    "summary": "Some contacts not found",
                    "value": {
                      "success": true,
                      "result_text": "Set to contacts. Some contacts not found",
                      "result_data": {
                        "processed": 3,
                        "not_found": [
                          "ghost@example.com"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or subscription not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                },
                "examples": {
                  "no_field": {
                    "summary": "Neither contact_ids nor contact_emails provided",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Either 'contact_ids' or 'contact_emails' must be provided"
                        ]
                      }
                    }
                  },
                  "both_fields": {
                    "summary": "Both contact_ids and contact_emails provided",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Provide either 'contact_ids' or 'contact_emails', not both"
                        ]
                      }
                    }
                  },
                  "subscription_not_found": {
                    "summary": "Subscription not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Remove contacts from a subscription",
        "description": "Bulk-removes contacts from a subscription. Accepts either `contact_ids` or `contact_emails` (not both). Maximum 1,000 items per request. The operation is idempotent — removing a contact that is not subscribed is a no-op.",
        "operationId": "removeSubscriptionContacts",
        "tags": [
          "Subscription Contacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactsRequest"
              },
              "examples": {
                "by_ids": {
                  "summary": "Remove by contact IDs",
                  "value": {
                    "contact_ids": [
                      101,
                      102
                    ]
                  }
                },
                "by_emails": {
                  "summary": "Remove by email addresses",
                  "value": {
                    "contact_emails": [
                      "alice@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation completed. Check `success` and `result_data` for details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactsSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Set to contacts",
                  "result_data": {
                    "processed": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or subscription not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2SubscriptionsValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/customfields": {
      "get": {
        "summary": "List custom fields",
        "description": "Returns a paginated array of contact-scoped custom fields for the authenticated account. Use `?limit` and `?skip` for pagination. Use `?result_type=kvp` for a flat id-to-name map.",
        "operationId": "listCustomFields",
        "tags": [
          "Custom Fields"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2CustomfieldsLimit"
          },
          {
            "$ref": "#/components/parameters/V2CustomfieldsSkip"
          },
          {
            "$ref": "#/components/parameters/V2CustomfieldsFields"
          },
          {
            "$ref": "#/components/parameters/V2CustomfieldsResultType"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response. Returns an array of custom fields, or a key-value object when `result_type=kvp` is used.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomField"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of field ID to field name. Returned when `result_type=kvp` is used.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "1",
                        "name": "phone_number",
                        "description": "Customer's mobile number",
                        "field_type": "text"
                      },
                      {
                        "id": "2",
                        "name": "birthday",
                        "description": "",
                        "field_type": "date"
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "1": "phone_number",
                      "2": "birthday"
                    }
                  },
                  "empty": {
                    "summary": "No custom fields defined",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a custom field",
        "description": "Creates a new contact-scoped custom field. `name` and `field_type` are required. The `field_type` cannot be changed after creation.",
        "operationId": "createCustomField",
        "tags": [
          "Custom Fields"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomFieldCreate"
              },
              "examples": {
                "minimal": {
                  "summary": "Minimal (required fields only)",
                  "value": {
                    "name": "phone_number",
                    "field_type": "text"
                  }
                },
                "full": {
                  "summary": "With optional description",
                  "value": {
                    "name": "phone_number",
                    "field_type": "text",
                    "description": "Customer's mobile phone number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Custom field created successfully. Returns the new field object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "42",
                    "name": "phone_number",
                    "description": "Customer's mobile phone number",
                    "field_type": "text"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsValidationErrorResponse"
                },
                "examples": {
                  "missing_fields": {
                    "summary": "Required fields missing",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' is required",
                          "field_type is required"
                        ]
                      }
                    }
                  },
                  "invalid_name": {
                    "summary": "Name contains invalid characters",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only alphanumeric and underscore characters are permitted in the name field"
                        ]
                      }
                    }
                  },
                  "invalid_field_type": {
                    "summary": "Invalid field_type value",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'field_type'; allowed values are: text, note, date, url"
                        ]
                      }
                    }
                  },
                  "duplicate_name": {
                    "summary": "Name already exists",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "A custom field named 'phone_number' already exists"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/customfields/{id}": {
      "get": {
        "summary": "Get a single custom field",
        "description": "Returns a single custom field by its ID.",
        "operationId": "getCustomField",
        "tags": [
          "Custom Fields"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CustomFieldId"
          },
          {
            "$ref": "#/components/parameters/V2CustomfieldsFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested custom field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomField"
                },
                "example": {
                  "id": "42",
                  "name": "phone_number",
                  "description": "Customer's mobile phone number",
                  "field_type": "text"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/V2CustomfieldsNotFoundGet"
          }
        }
      },
      "patch": {
        "summary": "Update a custom field",
        "description": "Updates the `name` and/or `description` of an existing custom field. `field_type` cannot be changed after creation.",
        "operationId": "updateCustomField",
        "tags": [
          "Custom Fields"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CustomFieldId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomFieldUpdate"
              },
              "examples": {
                "update_description": {
                  "summary": "Update description only",
                  "value": {
                    "description": "Updated description for the field"
                  }
                },
                "update_both": {
                  "summary": "Update name and description",
                  "value": {
                    "name": "mobile_number",
                    "description": "Customer's mobile number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Custom field updated successfully. Returns the updated field object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "42",
                    "name": "phone_number",
                    "description": "Updated description for the field",
                    "field_type": "text"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or field not found. All errors use the validation envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsValidationErrorResponse"
                },
                "examples": {
                  "field_type_immutable": {
                    "summary": "Attempted to change field_type",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'field_type' cannot be changed after creation"
                        ]
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Custom field not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '42' not found"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a custom field",
        "description": "Permanently deletes a custom field and **all associated contact values**. This action cannot be undone.",
        "operationId": "deleteCustomField",
        "tags": [
          "Custom Fields"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CustomFieldId"
          }
        ],
        "responses": {
          "200": {
            "description": "Custom field deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2CustomfieldsDeleteSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Deleted",
                  "result_data": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/V2CustomfieldsNotFoundMutate"
          }
        }
      }
    },
    "/v2/groups/{type}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/GroupType"
        }
      ],
      "get": {
        "summary": "List groups",
        "description": "Returns a paginated array of groups for the authenticated account and the specified item type, sorted alphabetically by name. Supports filtering by `id`, `name`, and `parent_group`. Use `?result_type=kvp` for a flat id-to-name map.",
        "operationId": "listGroups",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2GroupsLimit"
          },
          {
            "$ref": "#/components/parameters/V2GroupsSkip"
          },
          {
            "$ref": "#/components/parameters/V2GroupsFields"
          },
          {
            "$ref": "#/components/parameters/V2GroupsResultType"
          },
          {
            "name": "filter[id]",
            "in": "query",
            "description": "Filter by group ID. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[name]",
            "in": "query",
            "description": "Filter by group name. Supports `(ne)` operator.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[parent_group]",
            "in": "query",
            "description": "Filter by parent group name or numeric ID. Supports `(ne)` operator. Pass `\"\"` or `0` to filter for top-level groups only.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort[name]",
            "in": "query",
            "description": "Sort by name. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of groups.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of group ID to name.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "7",
                        "name": "Graduate Studies",
                        "parent_group": "Academic Programs"
                      },
                      {
                        "id": "3",
                        "name": "Student Life",
                        "parent_group": ""
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "3": "Student Life",
                      "7": "Graduate Studies"
                    }
                  },
                  "empty": {
                    "summary": "No groups",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2GroupsValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a group",
        "description": "Creates a new group of the specified type. `name` is required. Optionally nest the group under a parent by providing `parent_group` as a group name or ID. The parent must itself be a top-level group — only one level of nesting is supported.",
        "operationId": "createGroup",
        "tags": [
          "Groups"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupCreate"
              },
              "examples": {
                "top_level": {
                  "summary": "Create a top-level group",
                  "value": {
                    "name": "Academic Programs"
                  }
                },
                "subgroup_by_name": {
                  "summary": "Create a subgroup by parent name",
                  "value": {
                    "name": "Graduate Studies",
                    "parent_group": "Academic Programs"
                  }
                },
                "subgroup_by_id": {
                  "summary": "Create a subgroup by parent ID",
                  "value": {
                    "name": "Graduate Studies",
                    "parent_group": 7
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Group created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2GroupsCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "8",
                    "name": "Graduate Studies",
                    "parent_group": "Academic Programs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2GroupsValidationErrorResponse"
                },
                "examples": {
                  "name_required": {
                    "summary": "Missing name",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' is required"
                        ]
                      }
                    }
                  },
                  "parent_not_found": {
                    "summary": "Parent group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The 'parent_group' 'Nonexistent Group' not found"
                        ]
                      }
                    }
                  },
                  "parent_nested": {
                    "summary": "Parent group is itself a subgroup",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'parent_group' cannot be a nested group (maximum depth is 1)"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/groups/{type}/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/GroupType"
        },
        {
          "$ref": "#/components/parameters/GroupId"
        }
      ],
      "get": {
        "summary": "Get a single group",
        "description": "Returns a single group by its ID. Returns an empty array (`[]`) if the ID is not found.",
        "operationId": "getGroup",
        "tags": [
          "Groups"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2GroupsFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested group, or an empty array if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Group"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Group found",
                    "value": {
                      "id": "7",
                      "name": "Graduate Studies",
                      "parent_group": "Academic Programs"
                    }
                  },
                  "not_found": {
                    "summary": "Group not found",
                    "value": []
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a group",
        "description": "Updates one or more fields on an existing group. `name` and `parent_group` are updatable. If `id` is included in the body, it must match the `{id}` in the URL — if they differ, a validation envelope is returned. Pass `null` or `\"\"` for `parent_group` to promote the group to top-level.",
        "operationId": "updateGroup",
        "tags": [
          "Groups"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupUpdate"
              },
              "examples": {
                "rename": {
                  "summary": "Rename the group",
                  "value": {
                    "name": "Updated Group Name"
                  }
                },
                "assign_parent": {
                  "summary": "Assign a parent group",
                  "value": {
                    "parent_group": "Academic Programs"
                  }
                },
                "remove_parent_null": {
                  "summary": "Remove parent (promote to top-level) using null",
                  "value": {
                    "parent_group": null
                  }
                },
                "remove_parent_empty": {
                  "summary": "Remove parent (promote to top-level) using empty string",
                  "value": {
                    "parent_group": ""
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Group updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2GroupsUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "7",
                    "name": "Graduate Studies",
                    "parent_group": "Academic Programs"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, group not found, or id mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2GroupsValidationErrorResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  },
                  "name_empty": {
                    "summary": "name cannot be empty",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' cannot be empty"
                        ]
                      }
                    }
                  },
                  "parent_not_found": {
                    "summary": "Parent group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The 'parent_group' 'Nonexistent Group' not found"
                        ]
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "id in body does not match URL id",
                    "value": {
                      "success": false,
                      "result_text": "Provided 'id': 7 does not match 'id' in request body (999)",
                      "result_data": []
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a group",
        "description": "Permanently deletes a group. **The group must be empty** — it cannot have subgroups or assigned items (tags, subscriptions, messages, or templates depending on type). The same validation error is returned in both cases. Remove all subgroups and reassign or delete all assigned items before deleting the group.",
        "operationId": "deleteGroup",
        "tags": [
          "Groups"
        ],
        "responses": {
          "200": {
            "description": "Group deleted, or a validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2GroupsDeleteSuccessResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2GroupsValidationErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "success": {
                    "summary": "Deleted successfully",
                    "value": {
                      "success": true,
                      "result_text": "Deleted",
                      "result_data": []
                    }
                  },
                  "not_empty": {
                    "summary": "Group has subgroups or assigned items",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only empty groups can be deleted"
                        ]
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Group not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/templates": {
      "get": {
        "summary": "List templates",
        "description": "Returns a paginated array of templates for the authenticated account, sorted by most recently modified first by default. Supports sorting via `sort[]` query parameters. Supports filtering by `id`, `name`, `status`, `language`, and `group`. Use `?result_type=kvp` for a flat id-to-name map.",
        "operationId": "listTemplates",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2TemplatesLimit"
          },
          {
            "$ref": "#/components/parameters/V2TemplatesSkip"
          },
          {
            "$ref": "#/components/parameters/V2TemplatesFields"
          },
          {
            "$ref": "#/components/parameters/V2TemplatesResultType"
          },
          {
            "name": "filter[name]",
            "in": "query",
            "description": "Filter by template name. Supports operators: `(ne)`, `(in)`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "Filter by status. Valid values: `published`, `unpublished`.",
            "schema": {
              "type": "string",
              "enum": [
                "published",
                "unpublished"
              ]
            }
          },
          {
            "name": "filter[language]",
            "in": "query",
            "description": "Filter by language. Accepts a two-letter IETF code (e.g. `en`, `fr`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[group]",
            "in": "query",
            "description": "Filter by group name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[id]",
            "in": "query",
            "description": "Filter by template ID.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "sort[name]",
            "in": "query",
            "description": "Sort by name. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort[modified_time]",
            "in": "query",
            "description": "Sort by last modified time. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort[create_time]",
            "in": "query",
            "description": "Sort by creation time. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort[language]",
            "in": "query",
            "description": "Sort by language. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort[group]",
            "in": "query",
            "description": "Sort by group name. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "sort[status]",
            "in": "query",
            "description": "Sort by status. Value: `asc` or `desc`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of templates.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of template ID to name.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": 1,
                        "external_id": "a1b2c3d4e5f6",
                        "preview_url": "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6",
                        "name": "Welcome Email",
                        "status": "published",
                        "language": "en",
                        "group": "Onboarding",
                        "create_time": "2026-04-01 09:00:00",
                        "modified_time": "2026-04-10 18:00:00"
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "1": "Welcome Email",
                      "2": "Monthly Newsletter"
                    }
                  },
                  "empty": {
                    "summary": "No templates",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a template",
        "description": "Creates a new email template. Either `html` or `template_id` must be provided. `name` is optional — if omitted, it is auto-generated as `API Template {timestamp}`. New templates are always created with `status: published` regardless of any `status` value in the request body.",
        "operationId": "createTemplate",
        "tags": [
          "Templates"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplateCreate"
              },
              "examples": {
                "with_html": {
                  "summary": "Create with inline HTML",
                  "value": {
                    "name": "Welcome Email",
                    "html": "<html><body><h1>Welcome!</h1></body></html>",
                    "language": "en",
                    "group": "Onboarding"
                  }
                },
                "from_template": {
                  "summary": "Copy from existing template",
                  "value": {
                    "name": "Welcome Email Copy",
                    "template_id": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Template created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": 42,
                    "external_id": "a1b2c3d4e5f6",
                    "preview_url": "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6",
                    "name": "Welcome Email",
                    "status": "published",
                    "language": "en",
                    "group": "Onboarding",
                    "create_time": "2026-04-11 09:00:00",
                    "modified_time": "2026-04-11 09:00:00"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesValidationErrorResponse"
                },
                "examples": {
                  "missing_content": {
                    "summary": "Neither html nor template_id provided",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "One of the fields 'template_id' or 'html' must be provided in order to create a template"
                        ]
                      }
                    }
                  },
                  "name_too_short": {
                    "summary": "Name fewer than 3 characters",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' must be at least 3 characters long"
                        ]
                      }
                    }
                  },
                  "invalid_language": {
                    "summary": "Unrecognised language code",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'language'"
                        ],
                        "allowed_values": {
                          "language": {
                            "1": "English",
                            "2": "French"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/templates/{id}": {
      "get": {
        "summary": "Get a single template",
        "description": "Returns a single template by its ID. Add `?include_html=1` to include the full HTML content in the response.",
        "operationId": "getTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TemplateId"
          },
          {
            "$ref": "#/components/parameters/IncludeHTML"
          },
          {
            "$ref": "#/components/parameters/V2TemplatesFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested template. Includes `html` field only when `?include_html=1` is set.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Template"
                    },
                    {
                      "$ref": "#/components/schemas/TemplateWithHTML"
                    }
                  ]
                },
                "examples": {
                  "default": {
                    "summary": "Standard response",
                    "value": {
                      "id": 42,
                      "external_id": "a1b2c3d4e5f6",
                      "preview_url": "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6",
                      "name": "Welcome Email",
                      "status": "published",
                      "language": "en",
                      "group": "",
                      "create_time": "2026-04-11 09:00:00",
                      "modified_time": "2026-04-11 09:00:00"
                    }
                  },
                  "with_html": {
                    "summary": "With ?include_html=1",
                    "value": {
                      "id": 42,
                      "external_id": "a1b2c3d4e5f6",
                      "preview_url": "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6",
                      "name": "Welcome Email",
                      "status": "published",
                      "language": "en",
                      "group": "",
                      "create_time": "2026-04-11 09:00:00",
                      "modified_time": "2026-04-11 09:00:00",
                      "html": "<html><body><h1>Welcome!</h1></body></html>"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/V2TemplatesNotFoundGet"
          }
        }
      },
      "patch": {
        "summary": "Update a template",
        "description": "Updates one or more fields on an existing template. `name`, `status`, `language`, `group`, and `html` are updatable. `external_id`, `preview_url`, `create_time`, and `modified_time` are silently ignored if included. If `id` is included in the request body, it must match the `{id}` in the URL — if they differ, a validation error is returned.",
        "operationId": "updateTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TemplateId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplateUpdate"
              },
              "examples": {
                "update_name": {
                  "summary": "Rename the template",
                  "value": {
                    "name": "Updated Welcome Email"
                  }
                },
                "unpublish": {
                  "summary": "Unpublish a template",
                  "value": {
                    "status": "unpublished"
                  }
                },
                "update_html": {
                  "summary": "Replace HTML content",
                  "value": {
                    "html": "<html><body><h1>New content</h1></body></html>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Template updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": 42,
                    "external_id": "a1b2c3d4e5f6",
                    "preview_url": "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6",
                    "name": "Updated Welcome Email",
                    "status": "published",
                    "language": "en",
                    "group": "",
                    "create_time": "2026-04-11 09:00:00",
                    "modified_time": "2026-04-11 15:00:00"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or template not found. All errors use the validation envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesValidationErrorResponse"
                },
                "examples": {
                  "name_too_short": {
                    "summary": "Name too short",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' must be at least 3 characters long"
                        ]
                      }
                    }
                  },
                  "invalid_status": {
                    "summary": "Invalid status value",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'status'"
                        ],
                        "allowed_values": {
                          "status": [
                            "published",
                            "unpublished"
                          ]
                        }
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Template not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Item with id '999999' not found"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a template",
        "description": "Soft-deletes a template. The template is archived and no longer returned by the API, but the underlying data is not immediately purged.",
        "operationId": "deleteTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TemplateId"
          }
        ],
        "responses": {
          "200": {
            "description": "Template deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesDeleteSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Deleted",
                  "result_data": []
                }
              }
            }
          },
          "400": {
            "description": "The template ID was not found. Returns a validation error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2TemplatesValidationErrorResponse"
                },
                "example": {
                  "success": false,
                  "result_text": "Validation failed, see 'result_data' -> 'errors'",
                  "result_data": {
                    "errors": [
                      "Item with id '999999' not found"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v2/messages": {
      "get": {
        "summary": "List messages",
        "description": "Returns a JSON array of messages, most recently modified first, or `[]` when none match. The list contains every message in the account that has not been deleted, in every status (drafts, scheduled and sent messages, messages published to the archive, and the `Nurture` and `Transactional` messages set up in Envoke). Templates and deleted messages are not included; there is no filter by message type. `html` is never included. Supports filtering, sorting, pagination, and field selection. `limit=1` returns a bare object (or `[]`). Filter operators: equals and `(ne)` on every filterable field, plus `(or)` and `(not)` groups and numbered groups; each also has a `$` spelling (`$ne`, `$or`, `$not`). `(in)`, `(regex)`, `(gt)`, `(lt)`, `(gte)`, and `(lte)` are not supported on any message field and fail with an error naming the SQL operator (`'IN' is not a valid operator for the field 'status'`; `REGEXP`, `>`, `<`, `>=`, `<=` for the others); to match one of several values, use `(or)`. Filterable: `id`, `external_id`, `name`, `subject`, `status`, `language`, `recipient_language`, `publish_archive`, `utm_tag_links`. `create_time` and `modified_time` are sortable only. `group`, `send_time`, `sender`, `reply_to`, `recipient_subscription`, `recipient_tag`, and `recipient_segment` are not accepted as filter or sort fields; to find messages by one of these, list with `fields` and select on your side. When the request has no `filter` parameter, every query parameter other than `limit`, `skip`, `sort`, `fields`, `result_type`, and `include_html` is treated as a filter, so `?status=Sent` equals `?filter[status]=Sent`, and any other parameter is rejected (`Field 'folder' is not valid for filtering`); when `filter` is present, the other parameters are ignored as filters. Invalid parameters return 400: the `limit`, `skip`, and `sort` format errors first, then the filter and sort field errors listed together.",
        "operationId": "listMessages",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesLimit"
          },
          {
            "$ref": "#/components/parameters/V2MessagesSkip"
          },
          {
            "$ref": "#/components/parameters/V2MessagesFields"
          },
          {
            "$ref": "#/components/parameters/V2MessagesResultType"
          },
          {
            "name": "filter[{field}]",
            "in": "query",
            "description": "`id`, `external_id`, `name`, `subject`. Operators: equals and `(ne)`, e.g. `filter[subject]=Our spring season is here`. `name` and `subject` match the whole value, ignoring case. An unknown field gives `Field '{field}' is not valid for filtering` (an unrecognised operator such as `(foo)` gives `Field '(foo)' is not valid for filtering`); a list given to an operator (`filter[name][(ne)][]=Gala`) gives `Field 'name' requires a scalar value`.",
            "schema": {
              "type": "string"
            },
            "example": "Spring Season Announcement"
          },
          {
            "name": "filter[status]",
            "in": "query",
            "description": "A status name or code. Names are matched ignoring case and surrounding spaces, with a space and an underscore treated as the same (`Sent`, `sent`, `AB test in progress`, `ab_test_in_progress`). Codes: `Draft` 0, `Scheduled` 1, `AB test in progress` 2, `Queuing` 4, `Sending` 5, `Transactional` 7, `Nurture` 8, `Sent` 9; a code is used as given, so a number that is not a status code (e.g. `3`) is accepted and matches nothing. Operators: equals and `(ne)`, with names or codes. An unknown name gives `Invalid 'status' provided (possible values: 'Draft', 'Scheduled', 'AB test in progress', 'Queuing', 'Sending', 'Transactional', 'Nurture', 'Sent')`. `(in)` is not supported; use `(or)`.",
            "schema": {
              "type": "string"
            },
            "example": "Sent"
          },
          {
            "name": "filter[language]",
            "in": "query",
            "description": "Also `filter[recipient_language]`. Takes the language ID, not the code: `1` for English, `3` for French. `filter[language]=en` does not match English messages. Operators: equals and `(ne)`.",
            "schema": {
              "type": "integer",
              "enum": [
                1,
                3
              ]
            },
            "example": 3
          },
          {
            "name": "filter[publish_archive]",
            "in": "query",
            "description": "Also `filter[utm_tag_links]`. Takes the stored value. `publish_archive`: `2` for messages published to the archive, `1` for the rest. `utm_tag_links`: `1` for UTM tagging on, `0` for off. `true` and `false` do not work in these filters. Operators: equals and `(ne)`.",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1,
                2
              ]
            },
            "example": 2
          },
          {
            "name": "filter[(or)]",
            "in": "query",
            "description": "Matches if any of the numbered groups inside match: `filter[(or)][0][status]=Sent&filter[(or)][1][status]=Scheduled`. `$or` is the same.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[(not)]",
            "in": "query",
            "description": "Matches if the conditions inside do not all match: `filter[(not)][status]=Sent&filter[(not)][language]=3` excludes messages that are both sent and French. `$not` is the same.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[{n}]",
            "in": "query",
            "description": "Numbered AND group, used to repeat a field: `filter[0][status][(ne)]=Draft&filter[1][status][(ne)]=Sent`. Separate filters are ANDed; groups inside `(or)` are numbered the same way.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort[{field}]",
            "in": "query",
            "description": "Sort direction `asc` or `desc` (case-insensitive), or `1` or `-1`. Repeat for a secondary sort. A `sort` replaces the default order (`modified_time` descending). Sortable fields: `id`, `external_id`, `name`, `subject`, `status` (by status code), `language` and `recipient_language` (by language ID), `publish_archive`, `utm_tag_links`, `create_time`, `modified_time`. A field that does not exist gives `Field '{field}' is not valid for sorting`; a bad direction gives `'{value}' is not a valid sort direction`; `sort` without a field (`?sort=name`) gives `Invalid 'sort': 'name'`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc",
                "ASC",
                "DESC",
                "1",
                "-1"
              ]
            },
            "example": "asc"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching messages: an array, a key-value object with `result_type=kvp`, or a single object with `limit=1`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Message"
                    },
                    {
                      "type": "object",
                      "description": "Message ID to name (`result_type=kvp`).",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "2001",
                        "external_id": "3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                        "preview_url": "https://engage.envoke.com/m/a7c3e9f1b5d2/m/3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                        "create_time": "2026-10-06 10:15:42",
                        "modified_time": "2026-10-06 11:02:08",
                        "name": "Spring Season Announcement",
                        "group": "Productions",
                        "status": "Scheduled",
                        "send_time": "2026-10-20 09:00:00",
                        "sender": "Box Office <boxoffice@example.org>",
                        "reply_to": "Front of House <frontofhouse@example.org>",
                        "subject": "Our spring season is here",
                        "language": "en",
                        "recipient_subscription": "Season News",
                        "recipient_tag": "",
                        "recipient_segment": "",
                        "recipient_language": "",
                        "publish_archive": true,
                        "utm_tag_links": true
                      }
                    ]
                  },
                  "fields": {
                    "summary": "Selected fields (fields=id,name,status,send_time)",
                    "value": [
                      {
                        "id": "2002",
                        "name": "Auditions Open",
                        "status": "Draft",
                        "send_time": ""
                      },
                      {
                        "id": "2001",
                        "name": "Spring Season Announcement",
                        "status": "Scheduled",
                        "send_time": "2026-10-20 09:00:00"
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value response (result_type=kvp)",
                    "value": {
                      "2001": "Spring Season Announcement",
                      "2002": "Auditions Open"
                    }
                  },
                  "sent_or_scheduled": {
                    "summary": "?filter[(or)][0][status]=Sent&filter[(or)][1][status]=Scheduled&fields=id,name,status",
                    "value": [
                      {
                        "id": "2005",
                        "name": "Pirates of Penzance - Cast Notice",
                        "status": "Sent"
                      },
                      {
                        "id": "2001",
                        "name": "Spring Season Announcement",
                        "status": "Scheduled"
                      }
                    ]
                  },
                  "single": {
                    "summary": "limit=1 returns an object (?name=Auditions%20Open&fields=id,status&limit=1)",
                    "value": {
                      "id": "2002",
                      "status": "Draft"
                    }
                  },
                  "empty": {
                    "summary": "No messages match",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid list parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                },
                "examples": {
                  "filter_errors": {
                    "summary": "Unknown field and unsupported operator",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Field 'folder' is not valid for filtering",
                          "'IN' is not a valid operator for the field 'status'"
                        ]
                      }
                    }
                  },
                  "invalid_status": {
                    "summary": "Not a status name",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'status' provided (possible values: 'Draft', 'Scheduled', 'AB test in progress', 'Queuing', 'Sending', 'Transactional', 'Nurture', 'Sent')"
                        ]
                      }
                    }
                  },
                  "paging_errors": {
                    "summary": "Bad limit and skip",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'limit': 'ten'",
                          "Invalid 'skip': 'abc'"
                        ]
                      }
                    }
                  },
                  "sort_errors": {
                    "summary": "Unknown sort field and bad direction",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Field 'folder' is not valid for sorting",
                          "'up' is not a valid sort direction"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      },
      "post": {
        "summary": "Create a message",
        "description": "Creates a draft message and returns it in full, without `html`. Only `html` or `template_id` is required. Setting `send_time` stores it but does not schedule the message; to create and send in one request, use `POST /messages/send`. Read-only fields are silently ignored. A body that is not valid JSON returns the plain envelope `Invalid JSON`; no body, an empty body, or `{}` returns `Request body may not be empty`. If the content or the message cannot be saved, the plain envelope carries `Message content error` or `insert error: ` followed by the database error.",
        "operationId": "createMessage",
        "tags": [
          "Messages"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              },
              "examples": {
                "minimal": {
                  "summary": "HTML only (a Draft named API Message {time}, default sender, no subject or recipients)",
                  "value": {
                    "html": "<html><body><h1>Auditions Open</h1><p>Auditions for our spring production start Monday.</p></body></html>"
                  }
                },
                "full": {
                  "summary": "Every setting",
                  "value": {
                    "name": "Spring Season Announcement",
                    "group": "Productions",
                    "subject": "Our spring season is here",
                    "sender": "boxoffice@example.org",
                    "reply_to": 413,
                    "language": "en",
                    "recipient_subscription": "Season News",
                    "publish_archive": true,
                    "utm_tag_links": true,
                    "html": "<html><body><h1>Spring Season</h1><p>Four shows, one season pass.</p></body></html>"
                  }
                },
                "template_replace": {
                  "summary": "From a template, with its placeholders filled in",
                  "value": {
                    "name": "Pirates of Penzance - Opening Night",
                    "template_id": 1901,
                    "subject": "Opening night is almost here",
                    "replace": {
                      "{SHOW_TITLE}": "The Pirates of Penzance",
                      "{CURTAIN_TIME}": "7:30 p.m."
                    }
                  }
                },
                "master_template": {
                  "summary": "A block of HTML inserted into a master template",
                  "value": {
                    "name": "Weekly Show Listings",
                    "template_id": 1902,
                    "replace": {
                      "{SHOW_LISTINGS}": "<h2>This week</h2><p>Fri 7:30 p.m. - The Pirates of Penzance</p><p>Sat 2:00 p.m. - Matinee</p>"
                    }
                  }
                },
                "own_html_replace": {
                  "summary": "Your own HTML with placeholders",
                  "value": {
                    "name": "Opening Date Notice",
                    "html": "<html><body><p>Dear patron, {SHOW_TITLE} opens on {OPENING_DATE}.</p></body></html>",
                    "replace": {
                      "{SHOW_TITLE}": "The Pirates of Penzance",
                      "{OPENING_DATE}": "Friday, October 23"
                    }
                  }
                },
                "narrowed": {
                  "summary": "To a subscription, narrowed by a tag",
                  "value": {
                    "name": "Cast Party Invitation",
                    "subject": "You are invited to the cast party",
                    "recipient_subscription": "Season News",
                    "recipient_tag": "Cast Call",
                    "html": "<html><body><p>Join us after the final show.</p></body></html>"
                  }
                },
                "send_time": {
                  "summary": "A draft with a stored send time (stays a Draft)",
                  "value": {
                    "name": "Subscriber Presale",
                    "subject": "Presale opens Tuesday at 9",
                    "recipient_subscription": "Season News",
                    "send_time": "2026-10-20 09:00:00",
                    "html": "<html><body><p>Subscribers get first pick of seats.</p></body></html>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created. `result_data` is the full message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "2001",
                    "external_id": "3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                    "preview_url": "https://engage.envoke.com/m/a7c3e9f1b5d2/m/3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                    "create_time": "2026-10-06 10:15:42",
                    "modified_time": "2026-10-06 10:15:42",
                    "name": "Spring Season Announcement",
                    "group": "Productions",
                    "status": "Draft",
                    "send_time": "",
                    "sender": "Box Office <boxoffice@example.org>",
                    "reply_to": "Front of House <frontofhouse@example.org>",
                    "subject": "Our spring season is here",
                    "language": "en",
                    "recipient_subscription": "Season News",
                    "recipient_tag": "",
                    "recipient_segment": "",
                    "recipient_language": "",
                    "publish_archive": true,
                    "utm_tag_links": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, the body is not valid JSON, or the message could not be saved.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "empty_body": {
                    "summary": "No body, an empty body, or {}",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Request body may not be empty"
                        ]
                      }
                    }
                  },
                  "missing_content": {
                    "summary": "Neither html nor template_id",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "One of the fields 'template_id' or 'html' must be provided in order to create a message"
                        ]
                      }
                    }
                  },
                  "tag_and_segment": {
                    "summary": "Tag and segment together",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided"
                        ]
                      }
                    }
                  },
                  "template_not_found": {
                    "summary": "template_id matches nothing",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Nothing found for 'template_id': 1999"
                        ]
                      }
                    }
                  },
                  "field_errors": {
                    "summary": "Field checks, collected",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'headline' is not a valid field",
                          "The field 'name' must be at least 3 characters long",
                          "The field 'subject' must be at least 6 characters long",
                          "The field 'send_time' must be in the future"
                        ]
                      }
                    }
                  },
                  "unknown_group": {
                    "summary": "Unknown group, with allowed_values",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Playbills'"
                        ],
                        "allowed_values": {
                          "group": {
                            "31": "Productions",
                            "32": "Newsletters"
                          }
                        }
                      }
                    }
                  },
                  "unknown_sender": {
                    "summary": "No identity has that address",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No sender identity found for email: 'tickets@example.org'"
                        ],
                        "allowed_values": {
                          "sender": {
                            "412": "Box Office <boxoffice@example.org>",
                            "413": "Front of House <frontofhouse@example.org>"
                          }
                        }
                      }
                    }
                  },
                  "invalid_language": {
                    "summary": "Unknown language",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'language': 'de'"
                        ],
                        "allowed_values": {
                          "language": {
                            "1": "en",
                            "3": "fr"
                          }
                        }
                      }
                    }
                  },
                  "invalid_replace": {
                    "summary": "replace is not an object of strings with {...} keys",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'replace' data"
                        ]
                      }
                    }
                  },
                  "html_too_large": {
                    "summary": "html over 204,800 bytes (checked before replace)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'html' may not be longer than 200kB"
                        ]
                      }
                    }
                  },
                  "invalid_sender": {
                    "summary": "Not an identity ID, Name <email>, or email address",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'sender': 'Box Office'"
                        ],
                        "allowed_values": {
                          "sender": {
                            "412": "Box Office <boxoffice@example.org>",
                            "413": "Front of House <frontofhouse@example.org>"
                          }
                        }
                      }
                    }
                  },
                  "unknown_tag": {
                    "summary": "Unknown recipient tag",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'recipient_tag': 'Cast List'"
                        ],
                        "allowed_values": {
                          "recipient_tag": {
                            "57": "Cast Call",
                            "58": "Crew"
                          }
                        }
                      }
                    }
                  },
                  "invalid_send_time": {
                    "summary": "send_time is not a date",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'send_time': 'next Friday evening'"
                        ]
                      }
                    }
                  },
                  "insert_error": {
                    "summary": "The message could not be saved (plain envelope; the database error follows the prefix)",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "insert error: {database error}"
                    }
                  },
                  "content_error": {
                    "summary": "The content could not be saved (plain envelope)",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Message content error"
                    }
                  },
                  "invalid_json": {
                    "summary": "Body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      }
    },
    "/v2/messages/send": {
      "post": {
        "summary": "Create and send a message",
        "description": "Creates a draft from a full create body and sends it, in one request. Without `send_time` the message is sent immediately (on the next run of Envoke's sending process); with it, at that time. The body must include content, a subject, and recipients; `sender` falls back to the account default. On success, `result_data.id` is an integer. If the create step fails, nothing is created and the create error is returned. If the create step succeeds but the send step fails (for example, no subject, or out of credits), the draft is kept and its ID is not returned; the response is the send error only. The zero-recipient response is the one exception: its `result_data.id` is the new draft's ID. Give each message a unique `name` so that a leftover draft can be found with `GET /messages?name=...`.",
        "operationId": "createAndSendMessage",
        "tags": [
          "Sending"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              },
              "examples": {
                "template_now": {
                  "summary": "From a template, sent immediately",
                  "value": {
                    "name": "Pirates of Penzance - Cast Notice",
                    "template_id": 1901,
                    "replace": {
                      "{SHOW_TITLE}": "The Pirates of Penzance",
                      "{CURTAIN_TIME}": "7:30 p.m."
                    },
                    "subject": "Call times for opening night",
                    "sender": "Box Office <boxoffice@example.org>",
                    "recipient_tag": "Cast Call"
                  }
                },
                "scheduled": {
                  "summary": "Scheduled for an account-local time",
                  "value": {
                    "name": "Subscriber Presale",
                    "subject": "Presale opens Tuesday at 9",
                    "recipient_subscription": "Season News",
                    "send_time": "2026-10-20 09:00:00",
                    "html": "<html><body><p>Subscribers get first pick of seats.</p></body></html>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message created and scheduled. `result_data.id` is an integer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateAndSendSuccessResponse"
                },
                "examples": {
                  "immediate": {
                    "summary": "Sent now (id is an integer)",
                    "value": {
                      "success": true,
                      "result_text": "Message scheduled for send",
                      "result_data": {
                        "id": 2005,
                        "send_time": "2026-10-06 14:05:31",
                        "recipients": {
                          "total_selected": 2,
                          "emails_to_be_sent": 2,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  },
                  "scheduled": {
                    "summary": "Scheduled with send_time in the body",
                    "value": {
                      "success": true,
                      "result_text": "Message scheduled for send",
                      "result_data": {
                        "id": 2007,
                        "send_time": "2026-10-20 09:00:00",
                        "recipients": {
                          "total_selected": 412,
                          "emails_to_be_sent": 377,
                          "excluded_unsubscribed_contacts": 29,
                          "excluded_invalid_emails": 2,
                          "excluded_bounced_emails": 4,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A create error (nothing created), a send error (the draft is kept), zero recipients (the draft is kept and its ID returned), or a body that is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/SendNoRecipientsResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "empty_body": {
                    "summary": "No body, an empty body, or {}",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Request body may not be empty"
                        ]
                      }
                    }
                  },
                  "create_error": {
                    "summary": "Create step failed; nothing created",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "One of the fields 'template_id' or 'html' must be provided in order to create a message"
                        ]
                      }
                    }
                  },
                  "send_error": {
                    "summary": "Send step failed; the draft is kept and its ID is not returned",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'subject' must be set"
                        ]
                      }
                    }
                  },
                  "out_of_credits": {
                    "summary": "Prepaid credit balance below emails_to_be_sent",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email can't be sent, the account is out of credits"
                        ]
                      }
                    }
                  },
                  "no_recipients": {
                    "summary": "No contacts selected; result_data.id is the new draft",
                    "value": {
                      "success": false,
                      "result_text": "Email can't be sent, no contacts selected",
                      "result_data": {
                        "id": 2006,
                        "recipients": {
                          "total_selected": 0,
                          "emails_to_be_sent": 0,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  },
                  "invalid_json": {
                    "summary": "Body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      }
    },
    "/v2/messages/{id}": {
      "get": {
        "summary": "Get a message",
        "description": "Returns a single message object, the same shape as one item of the list. `fields` selects fields; filter, sort, paging, and `result_type` parameters are ignored. Add `include_html=1` to append the HTML as the last key. If the ID is not found, or belongs to a deleted message or a template, the response is an empty array (`[]`) with HTTP 200.",
        "operationId": "getMessage",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesMessageId"
          },
          {
            "$ref": "#/components/parameters/IncludeHtml"
          },
          {
            "$ref": "#/components/parameters/V2MessagesFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The message, or `[]` when not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Message"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "with_html": {
                    "summary": "?include_html=1",
                    "value": {
                      "id": "2003",
                      "external_id": "9c4d2a7e1f5b3d8a6c0e4f2b7d9a1c5e",
                      "preview_url": "https://engage.envoke.com/m/a7c3e9f1b5d2/m/9c4d2a7e1f5b3d8a6c0e4f2b7d9a1c5e",
                      "create_time": "2026-10-06 10:40:11",
                      "modified_time": "2026-10-06 10:40:11",
                      "name": "Pirates of Penzance - Opening Night",
                      "group": "",
                      "status": "Draft",
                      "send_time": "",
                      "sender": "Box Office <boxoffice@example.org>",
                      "reply_to": null,
                      "subject": "",
                      "language": "en",
                      "recipient_subscription": "",
                      "recipient_tag": "",
                      "recipient_segment": "",
                      "recipient_language": "",
                      "publish_archive": false,
                      "utm_tag_links": false,
                      "html": "<html><body><h1>The Pirates of Penzance</h1><p>Curtain at 7:30 p.m.</p></body></html>"
                    }
                  },
                  "fields": {
                    "summary": "?fields=id,name,status",
                    "value": {
                      "id": "2001",
                      "name": "Spring Season Announcement",
                      "status": "Scheduled"
                    }
                  },
                  "not_found": {
                    "summary": "Not found, deleted, or a template",
                    "value": []
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      },
      "patch": {
        "summary": "Update a draft message",
        "description": "Changes a `Draft` message and returns it in full, without `html`. Send only the fields to change; the recipient fields are replaced as a set: when a request includes any one of `recipient_subscription`, `recipient_tag`, or `recipient_segment`, the other two are cleared unless they are also in the request (`recipient_language` is never cleared by this rule). `html` replaces the whole content; `replace` works only with `html` in the same request. Every successful update sets `modified_time`, even when no value changed. Read-only fields are silently ignored. A missing message (including a deleted message or a template) gives `No message found for 'id': {id}`. Body `id` different from the URL gives the plain envelope `Provided 'id': {id} does not match 'id' in request body ({body id})`. No body, an empty body, or `{}` gives `Request body may not be empty`.",
        "operationId": "updateMessage",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesMessageId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageUpdate"
              },
              "examples": {
                "subject_html": {
                  "summary": "Change the subject and HTML",
                  "value": {
                    "subject": "Auditions start Monday at 7",
                    "html": "<html><body><h1>Auditions Open</h1><p>Monday at 7 p.m. in the rehearsal hall.</p></body></html>"
                  }
                },
                "recipients": {
                  "summary": "Send to a tag instead of a subscription (the subscription is cleared)",
                  "value": {
                    "recipient_tag": "Cast Call"
                  }
                },
                "clear_reply_to": {
                  "summary": "Clear the reply-to (read back as null)",
                  "value": {
                    "reply_to": ""
                  }
                },
                "send_time": {
                  "summary": "Store a send time (stays a Draft)",
                  "value": {
                    "send_time": "2026-10-20 09:00:00"
                  }
                },
                "html_replace": {
                  "summary": "New HTML with placeholders",
                  "value": {
                    "html": "<html><body><h1>{SHOW_TITLE}</h1><p>Curtain at {CURTAIN_TIME}.</p></body></html>",
                    "replace": {
                      "{SHOW_TITLE}": "The Pirates of Penzance",
                      "{CURTAIN_TIME}": "8:00 p.m."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message updated. `result_data` is the full message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "2002",
                    "external_id": "5a9e3c1f7b2d4a6e8c0f1b3d5e7a9c2b",
                    "preview_url": "https://engage.envoke.com/m/a7c3e9f1b5d2/m/5a9e3c1f7b2d4a6e8c0f1b3d5e7a9c2b",
                    "create_time": "2026-10-06 10:22:09",
                    "modified_time": "2026-10-06 11:30:17",
                    "name": "Auditions Open",
                    "group": "",
                    "status": "Draft",
                    "send_time": "",
                    "sender": "Box Office <boxoffice@example.org>",
                    "reply_to": null,
                    "subject": "Auditions start Monday at 7",
                    "language": "en",
                    "recipient_subscription": "",
                    "recipient_tag": "Cast Call",
                    "recipient_segment": "",
                    "recipient_language": "",
                    "publish_archive": false,
                    "utm_tag_links": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, the ID in the body does not match, or the body is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "empty_body": {
                    "summary": "No body, an empty body, or {}",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Request body may not be empty"
                        ]
                      }
                    }
                  },
                  "html_too_large": {
                    "summary": "html over 204,800 bytes",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'html' may not be longer than 200kB"
                        ]
                      }
                    }
                  },
                  "tag_and_segment": {
                    "summary": "Tag and segment together",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided"
                        ]
                      }
                    }
                  },
                  "field_errors": {
                    "summary": "Field checks, collected (returned before any replace error)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'headline' is not a valid field",
                          "The field 'name' must be at least 3 characters long",
                          "Invalid 'recipient_tag': 'Cast List'"
                        ]
                      }
                    }
                  },
                  "invalid_replace": {
                    "summary": "replace is not an object of strings with {...} keys",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'replace' data"
                        ]
                      }
                    }
                  },
                  "past_send_time": {
                    "summary": "send_time in the past, or \"\" / null (read as now)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'send_time' must be in the future"
                        ]
                      }
                    }
                  },
                  "invalid_json": {
                    "summary": "Body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  },
                  "not_found": {
                    "summary": "No message with this ID",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No message found for 'id': 2099"
                        ]
                      }
                    }
                  },
                  "not_draft": {
                    "summary": "The message is not a Draft",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only 'Draft' messages can be updated, this message has the status: 'Scheduled'"
                        ]
                      }
                    }
                  },
                  "template_id": {
                    "summary": "template_id is create-only",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'template_id' is not valid for updating a message"
                        ]
                      }
                    }
                  },
                  "replace_without_html": {
                    "summary": "replace sent without html",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'html' is required for content 'replace' data"
                        ]
                      }
                    }
                  },
                  "number_zero": {
                    "summary": "A JSON number 0 does not clear a lookup field",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': '0'"
                        ],
                        "allowed_values": {
                          "group": {
                            "31": "Productions",
                            "32": "Newsletters"
                          }
                        }
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "Body id differs from the URL (plain envelope)",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Provided 'id': 2001 does not match 'id' in request body (2002)"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      },
      "delete": {
        "summary": "Delete a message",
        "description": "Moves the message to the Deleted folder in Envoke, where it can be restored only in the Envoke app until Envoke's scheduled cleanup removes it permanently. A GET for it then returns `[]` and it is no longer listed. Deleting a `Scheduled` message also sets it back to `Draft`, so it is not sent; there is no need to cancel it first. Messages in most statuses can be deleted, including `Sent` messages; a message that is `Sending`, a `Nurture` message in a nurture series, and Envoke system or offer messages cannot. Every response uses the plain envelope with `result_data: []`. If the delete fails on the server, `result_text` is `Delete error: ` followed by the error detail.",
        "operationId": "deleteMessage",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesMessageId"
          }
        ],
        "responses": {
          "200": {
            "description": "Message deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesDeleteResponse"
                },
                "example": {
                  "success": true,
                  "result_data": [],
                  "result_text": "Deleted"
                }
              }
            }
          },
          "400": {
            "description": "Not found, blocked, or a server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesDeleteResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "No message has this ID",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Item with id '2099' not found"
                    }
                  },
                  "sending": {
                    "summary": "The message is Sending",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Message: 'Spring Season Announcement' is currently sending"
                    }
                  },
                  "nurture_series": {
                    "summary": "Part of a nurture series",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "This message cannot be deleted because it's part of the automated series: \"Welcome Sequence\""
                    }
                  },
                  "system": {
                    "summary": "A message Envoke manages",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "System messages cannot be deleted"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      }
    },
    "/v2/messages/{id}/send": {
      "post": {
        "summary": "Send a draft message",
        "description": "Sends a `Draft` message now or at its send time. The body is optional and can contain any update field (most often `send_time`); the fields are saved first, exactly as a `PATCH` would, and stay saved even if the send then fails. After any update, the message must have at least one recipient field, a subject, and a sender; the three errors are returned together. `name` is not required. Send time: with `send_time` in the body, at that time (it must be in the future and is read as account-local time); with no `send_time` in the body and none stored, immediately (on the next run of Envoke's sending process); with no `send_time` in the body and one stored, at the stored time, which is not checked again, so a stored time that has passed (for example, on a cancelled message) sends the message on the next run. Send a new `send_time` when re-sending a cancelled message. The response's counts are an estimate; `result_data.id` is a string. When `emails_to_be_sent` is `0`, the message is not scheduled and the response is HTTP 400 with the counts. A prepaid account whose credit balance is below `emails_to_be_sent` gets `Email can't be sent, the account is out of credits`; a monthly-billed account not yet on a paid plan, with too few credits, gets `Email can't be sent, the account has not upgraded`; the message stays a `Draft`.",
        "operationId": "sendMessage",
        "tags": [
          "Sending"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesMessageId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/V2MessagesSendRequest"
              },
              "examples": {
                "scheduled": {
                  "summary": "Schedule for 9:00 a.m. account time (no offset or Z)",
                  "value": {
                    "send_time": "2026-10-20 09:00:00"
                  }
                },
                "recipients": {
                  "summary": "Change the recipients and send now",
                  "value": {
                    "recipient_tag": "Understudies"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message scheduled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2MessagesSendSuccessResponse"
                },
                "examples": {
                  "scheduled": {
                    "summary": "Scheduled for a future send_time",
                    "value": {
                      "success": true,
                      "result_text": "Message scheduled for send",
                      "result_data": {
                        "id": "2001",
                        "send_time": "2026-10-20 09:00:00",
                        "recipients": {
                          "total_selected": 412,
                          "emails_to_be_sent": 377,
                          "excluded_unsubscribed_contacts": 29,
                          "excluded_invalid_emails": 2,
                          "excluded_bounced_emails": 4,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  },
                  "immediate": {
                    "summary": "Sent now: send_time is the time of the request",
                    "value": {
                      "success": true,
                      "result_text": "Message scheduled for send",
                      "result_data": {
                        "id": "2002",
                        "send_time": "2026-10-06 11:41:27",
                        "recipients": {
                          "total_selected": 38,
                          "emails_to_be_sent": 35,
                          "excluded_unsubscribed_contacts": 2,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 1,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, zero recipients, the ID in the body does not match, or the body is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/SendNoRecipientsResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missing_settings": {
                    "summary": "No recipients, subject, or sender",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "At least one of the fields 'recipient_subscription', 'recipient_tag', or 'recipient_segment' must be set",
                          "The field 'subject' must be set",
                          "The field 'sender' must be set"
                        ]
                      }
                    }
                  },
                  "not_draft": {
                    "summary": "The message is not a Draft",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only 'Draft' messages can be updated, this message has the status: 'Scheduled'"
                        ]
                      }
                    }
                  },
                  "not_found": {
                    "summary": "No message with this ID",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No message found for 'id': 2099"
                        ]
                      }
                    }
                  },
                  "past_send_time": {
                    "summary": "send_time in the past",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'send_time' must be in the future"
                        ]
                      }
                    }
                  },
                  "out_of_credits": {
                    "summary": "Prepaid credit balance below emails_to_be_sent",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email can't be sent, the account is out of credits"
                        ]
                      }
                    }
                  },
                  "not_upgraded": {
                    "summary": "Monthly-billed account not yet on a paid plan",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email can't be sent, the account has not upgraded"
                        ]
                      }
                    }
                  },
                  "no_recipients": {
                    "summary": "No contacts selected; the message stays a Draft",
                    "value": {
                      "success": false,
                      "result_text": "Email can't be sent, no contacts selected",
                      "result_data": {
                        "id": "2004",
                        "recipients": {
                          "total_selected": 0,
                          "emails_to_be_sent": 0,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0,
                          "excluded_by_language": 0
                        }
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "Body id differs from the URL (plain envelope)",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Provided 'id': 2001 does not match 'id' in request body (2002)"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      }
    },
    "/v2/messages/{id}/cancel": {
      "post": {
        "summary": "Cancel a scheduled send",
        "description": "Returns a `Scheduled` message to `Draft` so that it will not be sent, and returns the message in full. The message keeps its `send_time`; sending it again with no `send_time` reuses the stored time, which may now be in the past. The body is optional; update fields in it are applied after the message is returned to `Draft`, and if one fails validation the message is already a `Draft` but the response is the validation error. The API also accepts cancel for `Queuing`, `Sending`, and `AB test in progress` messages and sets them to `Draft`, but emails already queued are still delivered: only cancel scheduled messages. Any other status gives `Sending cannot be cancelled for a message with 'status': {Status}`, with the status name not quoted; a missing ID gives the same error with the status left blank.",
        "operationId": "cancelMessage",
        "tags": [
          "Sending"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V2MessagesMessageId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/V2MessagesSendRequest"
              },
              "examples": {
                "empty": {
                  "summary": "No body",
                  "value": {}
                },
                "with_update": {
                  "summary": "Cancel and change the subject",
                  "value": {
                    "subject": "Presale moves to Wednesday"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message returned to `Draft`. `result_data` is the full message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Message reverted to draft status",
                  "result_data": {
                    "id": "2001",
                    "external_id": "3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                    "preview_url": "https://engage.envoke.com/m/a7c3e9f1b5d2/m/3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f",
                    "create_time": "2026-10-06 10:15:42",
                    "modified_time": "2026-10-06 11:45:03",
                    "name": "Spring Season Announcement",
                    "group": "Productions",
                    "status": "Draft",
                    "send_time": "2026-10-20 09:00:00",
                    "sender": "Box Office <boxoffice@example.org>",
                    "reply_to": "Front of House <frontofhouse@example.org>",
                    "subject": "Our spring season is here",
                    "language": "en",
                    "recipient_subscription": "Season News",
                    "recipient_tag": "",
                    "recipient_segment": "",
                    "recipient_language": "",
                    "publish_archive": true,
                    "utm_tag_links": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "The message cannot be cancelled, a body field failed validation, the ID in the body does not match, or the body is not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V2MessagesValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "wrong_status": {
                    "summary": "Not scheduled or sending",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Sending cannot be cancelled for a message with 'status': Draft"
                        ]
                      }
                    }
                  },
                  "not_found": {
                    "summary": "No message with this ID (status left blank)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Sending cannot be cancelled for a message with 'status': "
                        ]
                      }
                    }
                  },
                  "body_field_error": {
                    "summary": "A body field failed; the message is already a Draft",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Playbills'"
                        ],
                        "allowed_values": {
                          "group": {
                            "31": "Productions",
                            "32": "Newsletters"
                          }
                        }
                      }
                    }
                  },
                  "id_mismatch": {
                    "summary": "Body id differs from the URL (plain envelope)",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Provided 'id': 2001 does not match 'id' in request body (2002)"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2MessagesUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2MessagesForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2MessagesTooManyRequests"
          }
        }
      }
    },
    "/v1/send": {
      "post": {
        "summary": "Send a message",
        "description": "Creates a message and sends it now to up to 1,000 recipients, or, with `id`, sends an existing Send API message to up to 1,000 more. The body is a JSON object. No body, an empty body, `{}`, `[]`, or `null` returns `Request body may not be empty`; a body that is not valid JSON returns the plain error `Invalid JSON`. A successful request creates the message with status `Sending`, decides who is sent it, and queues the emails straight away; Envoke checks every minute for messages whose emails have all been handed off and marks them `Sent`. Order of checks on a new message: the Transactional API preference, the size of `html`, `template_id` not found, neither `html` nor `template_id` (each returned alone); then the subject, recipient, and field errors (collected and returned together); then each recipient is looked up and new addresses are created as contacts; then the credit check (a prepaid account whose balance is lower than `emails_to_be_sent` gets `Message not sent, the account is out of credits`; a monthly-billed account not yet on a paid plan, with too few credits, gets `Message not sent, the account has not upgraded`); then, when nobody can be sent the message, `Message not sent, there are no valid recipients`. The message is then readable through the Message API: `GET /v2/messages/{id}` shows `status` `Sending`, then `Sent`; `group` `API Messages` (or the group set); `send_time` the time of the request; `utm_tag_links` `false`; and `recipient_subscription`, `recipient_tag`, `recipient_segment`, and `recipient_language` empty, except that a mandatory message shows the mandatory consent subscription in `recipient_subscription`. With `include_html=1` the HTML still shows the `replace` placeholders. The Message API refuses to update or send it, because it is not a `Draft`; it can be deleted with `DELETE /v2/messages/{id}` once it is `Sent` (while it is `Sending`, delete fails with `Message: '{name}' is currently sending`), after which it can no longer be used with `id`. Only `POST /v1/send` (and `POST /v1/send.json`) exists.",
        "operationId": "sendEmail",
        "tags": [
          "Send"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/V1SendSendRequest"
                  },
                  {
                    "$ref": "#/components/schemas/SendToExistingRequest"
                  }
                ]
              },
              "examples": {
                "html_one_address": {
                  "summary": "Send HTML to one address, with every default",
                  "value": {
                    "subject": "Practice moved to 6:00 a.m.",
                    "html": "<html><body><p>Tomorrow's practice starts at 6:00 a.m. at the boathouse.</p></body></html>",
                    "recipients": [
                      "stroke.seat@example.org"
                    ]
                  }
                },
                "replace_per_recipient": {
                  "summary": "Personalise each email with replace",
                  "value": {
                    "name": "Head of the Valley - Crew Call-up",
                    "subject": "Crew call-up: Head of the Valley",
                    "sender": "Boathouse <boathouse@example.org>",
                    "html": "<html><body><p>You are racing in {BOAT_NAME}, seat {SEAT}.</p><p>Be at the boathouse by {CALL_TIME}.</p></body></html>",
                    "recipients": [
                      {
                        "email": "stroke.seat@example.org",
                        "replace": {
                          "{BOAT_NAME}": "Heron",
                          "{SEAT}": "8 (stroke)",
                          "{CALL_TIME}": "6:15 a.m."
                        }
                      },
                      {
                        "email": "bow.seat@example.org",
                        "replace": {
                          "{BOAT_NAME}": "Heron",
                          "{SEAT}": "1 (bow)",
                          "{CALL_TIME}": "6:15 a.m."
                        }
                      }
                    ]
                  }
                },
                "template_with_merge_fields": {
                  "summary": "Copy a template's HTML and fill it in, with merge fields",
                  "value": {
                    "name": "Spring Regatta Confirmation",
                    "subject": "Your Spring Regatta entry is confirmed",
                    "template_id": 1951,
                    "enable_merge_fields": true,
                    "recipients": [
                      {
                        "email": "stroke.seat@example.org",
                        "replace": {
                          "{RACE_NAME}": "Men's Eight",
                          "{START_TIME}": "9:40 a.m."
                        }
                      },
                      {
                        "email": "bow.seat@example.org",
                        "replace": {
                          "{RACE_NAME}": "Mixed Four",
                          "{START_TIME}": "11:05 a.m."
                        }
                      }
                    ]
                  }
                },
                "contact_ids": {
                  "summary": "Send to contacts by ID",
                  "value": {
                    "subject": "Erg test results are posted",
                    "html": "<html><body><p>Your erg test results are on the club board.</p></body></html>",
                    "recipient_contact_ids": [
                      7001,
                      7002,
                      7009
                    ]
                  }
                },
                "contact_ids_with_replace": {
                  "summary": "Send to contacts by ID, each with its own replace values",
                  "value": {
                    "subject": "Your seat for Saturday",
                    "html": "<html><body><p>Saturday you are in seat {SEAT}.</p></body></html>",
                    "recipient_contact_ids": [
                      {
                        "id": 7001,
                        "replace": {
                          "{SEAT}": "8"
                        }
                      },
                      {
                        "id": 7002,
                        "replace": {
                          "{SEAT}": "1"
                        }
                      }
                    ]
                  }
                },
                "every_option": {
                  "summary": "Set every option",
                  "value": {
                    "name": "Régate de printemps - Résultats",
                    "subject": "Résultats de la régate de printemps",
                    "group": "Crew Notices",
                    "sender": "boathouse@example.org",
                    "reply_to": 513,
                    "language": "fr",
                    "track_recipient_activity": true,
                    "publish_archive": true,
                    "html": "<html><body><p>Les résultats sont en ligne : <a href=\"https://www.example.org/resultats\">voir les résultats</a>.</p></body></html>",
                    "recipients": [
                      "stroke.seat@example.org",
                      "bow.seat@example.org"
                    ]
                  }
                },
                "mandatory": {
                  "summary": "Send a mandatory message",
                  "value": {
                    "name": "Updated Water Safety Policy",
                    "subject": "Updated water safety policy for all members",
                    "is_mandatory": true,
                    "html": "<html><body><p>The club's water safety policy has changed. Please read it before your next outing.</p></body></html>",
                    "recipients": [
                      "stroke.seat@example.org",
                      "bow.seat@example.org"
                    ]
                  }
                },
                "mixed": {
                  "summary": "Existing, new, unsubscribed, and invalid recipients",
                  "value": {
                    "name": "Learn to Row - Week 1",
                    "subject": "Welcome to Learn to Row",
                    "html": "<html><body><p>Week 1 starts Saturday at 8:00 a.m.</p></body></html>",
                    "recipients": [
                      "stroke.seat@example.org",
                      "novice.rower@example.org",
                      "cox@example.org",
                      "kvrc-not-an-email"
                    ]
                  }
                },
                "add_recipients": {
                  "summary": "Send an existing Send API message to more recipients",
                  "value": {
                    "id": 3006,
                    "recipients": [
                      "bow.seat@example.org",
                      "novice.rower@example.org"
                    ]
                  }
                },
                "add_recipients_with_replace": {
                  "summary": "A later batch fills the stored placeholders with new values",
                  "value": {
                    "id": 3002,
                    "recipients": [
                      {
                        "email": "spare.rower@example.org",
                        "replace": {
                          "{BOAT_NAME}": "Osprey",
                          "{SEAT}": "4",
                          "{CALL_TIME}": "7:00 a.m."
                        }
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "At least one recipient was sent the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendSendSuccessResponse"
                },
                "examples": {
                  "one_address": {
                    "summary": "One existing contact, sent",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3001,
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 1,
                          "emails_to_be_sent": 1,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7001,
                            "email": "stroke.seat@example.org",
                            "emails_to_be_sent": true
                          }
                        ]
                      }
                    }
                  },
                  "contact_ids": {
                    "summary": "By contact ID; 7009 does not exist",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3004,
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 3,
                          "emails_to_be_sent": 2,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_ids": 1,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7001,
                            "email": "stroke.seat@example.org",
                            "emails_to_be_sent": true
                          },
                          {
                            "id": 7002,
                            "email": "bow.seat@example.org",
                            "emails_to_be_sent": true
                          },
                          {
                            "excluded_invalid_ids": true
                          }
                        ]
                      }
                    }
                  },
                  "mandatory": {
                    "summary": "Mandatory: 7002 does not hold the mandatory consent subscription",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3007,
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 2,
                          "emails_to_be_sent": 1,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0,
                          "excluded_mandatory_consent": 1
                        },
                        "recipients": [
                          {
                            "id": 7001,
                            "email": "stroke.seat@example.org",
                            "emails_to_be_sent": true
                          },
                          {
                            "id": 7002,
                            "email": "bow.seat@example.org",
                            "excluded_mandatory_consent": true
                          }
                        ]
                      }
                    }
                  },
                  "mixed": {
                    "summary": "One existing and one new contact sent; one unsubscribed; one invalid",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3006,
                        "new_contacts_created": 1,
                        "summary": {
                          "total_selected": 4,
                          "emails_to_be_sent": 2,
                          "excluded_unsubscribed_contacts": 1,
                          "excluded_invalid_emails": 1,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7001,
                            "email": "stroke.seat@example.org",
                            "emails_to_be_sent": true
                          },
                          {
                            "id": 7005,
                            "email": "novice.rower@example.org",
                            "new_contacts_created": true,
                            "emails_to_be_sent": true
                          },
                          {
                            "id": 7003,
                            "email": "cox@example.org",
                            "excluded_unsubscribed_contacts": true
                          },
                          {
                            "email": "kvrc-not-an-email",
                            "excluded_invalid_emails": true
                          }
                        ]
                      }
                    }
                  },
                  "invalid_replace": {
                    "summary": "One recipient excluded for an invalid replace",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3002,
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 2,
                          "emails_to_be_sent": 1,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0,
                          "excluded_invalid_replace_data": 1
                        },
                        "recipients": [
                          {
                            "id": 7001,
                            "email": "stroke.seat@example.org",
                            "emails_to_be_sent": true
                          },
                          {
                            "id": 7002,
                            "email": "bow.seat@example.org",
                            "excluded_invalid_replace_data": true
                          }
                        ]
                      }
                    }
                  },
                  "add_recipients": {
                    "summary": "Existing message 3006 sent to one more contact",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": 3006,
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 1,
                          "emails_to_be_sent": 1,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7002,
                            "email": "bow.seat@example.org",
                            "emails_to_be_sent": true
                          }
                        ]
                      }
                    }
                  },
                  "add_recipients_string_id": {
                    "summary": "`id` sent as a string is echoed as a string",
                    "value": {
                      "success": true,
                      "result_text": "Message is sending",
                      "result_data": {
                        "id": "3006",
                        "new_contacts_created": 0,
                        "summary": {
                          "total_selected": 1,
                          "emails_to_be_sent": 1,
                          "excluded_unsubscribed_contacts": 0,
                          "excluded_invalid_emails": 0,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7002,
                            "email": "bow.seat@example.org",
                            "emails_to_be_sent": true
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, no valid recipients, a request-shape or content error, or an internal error.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1SendValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/NoValidRecipientsResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V1SendPlainErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/InternalErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "empty_body": {
                    "summary": "No body, an empty body, {}, [], or null",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Request body may not be empty"
                        ]
                      }
                    }
                  },
                  "preference_not_set": {
                    "summary": "The Transactional API preference is not set (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "A default consent status and description is required in order to use this API"
                        ]
                      }
                    }
                  },
                  "html_too_large": {
                    "summary": "html over 204,800 bytes (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'html' may not be longer than 200kB"
                        ]
                      }
                    }
                  },
                  "template_not_found": {
                    "summary": "No template or message with that ID (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Nothing found for 'template_id': 1999"
                        ]
                      }
                    }
                  },
                  "no_content": {
                    "summary": "Neither html nor template_id (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "One of the fields 'template_id' or 'html' must be provided in order to create a message"
                        ]
                      }
                    }
                  },
                  "collected": {
                    "summary": "Field errors returned together",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'subject' must be set",
                          "The field 'recipients' must be set",
                          "The field 'name' must be at least 3 characters long",
                          "'send_time' is not a valid field"
                        ]
                      }
                    }
                  },
                  "subject_empty": {
                    "summary": "subject \"\" returns both subject errors",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'subject' must be set",
                          "The field 'subject' must be at least 6 characters long"
                        ]
                      }
                    }
                  },
                  "not_supported": {
                    "summary": "Keys that are not Send API fields",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'send_time' is not a valid field",
                          "'utm_tag_links' is not a valid field",
                          "'replace' is not a valid field"
                        ]
                      }
                    }
                  },
                  "unknown_group": {
                    "summary": "Unknown group, with allowed_values",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'group': 'Regattas'"
                        ],
                        "allowed_values": {
                          "group": {
                            "41": "Crew Notices",
                            "42": "API Messages"
                          }
                        }
                      }
                    }
                  },
                  "sender_not_found": {
                    "summary": "No identity has that address",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No sender identity found for email: 'regatta@example.org'"
                        ],
                        "allowed_values": {
                          "sender": {
                            "512": "Boathouse <boathouse@example.org>",
                            "513": "Head Coach <coach@example.org>"
                          }
                        }
                      }
                    }
                  },
                  "unknown_language": {
                    "summary": "Unknown language",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'language': 'de'"
                        ],
                        "allowed_values": {
                          "language": {
                            "1": "en",
                            "3": "fr"
                          }
                        }
                      }
                    }
                  },
                  "recipients_shape": {
                    "summary": "First recipient object without email",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid 'recipients': items must have the field 'email'"
                        ]
                      }
                    }
                  },
                  "id_not_found": {
                    "summary": "id matches no message in the account (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "No message found for 'id': 3099"
                        ]
                      }
                    }
                  },
                  "id_not_api": {
                    "summary": "id is a message not created by the Send API (returned alone)",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Only messages created by the API are valid when providing an 'id'"
                        ]
                      }
                    }
                  },
                  "id_with_field": {
                    "summary": "Another field sent with id",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'subject' is not valid when providing an 'id'"
                        ]
                      }
                    }
                  },
                  "id_with_contact_ids": {
                    "summary": "recipient_contact_ids is not accepted with id",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'recipient_contact_ids' is not valid when providing an 'id'"
                        ]
                      }
                    }
                  },
                  "id_with_fields": {
                    "summary": "Several other fields sent with id",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The fields 'subject', 'html' are not valid when providing an 'id'"
                        ]
                      }
                    }
                  },
                  "out_of_credits": {
                    "summary": "Prepaid balance lower than emails_to_be_sent",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Message not sent, the account is out of credits"
                        ]
                      }
                    }
                  },
                  "not_upgraded": {
                    "summary": "Monthly-billed account not yet on a paid plan, with too few credits",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Message not sent, the account has not upgraded"
                        ]
                      }
                    }
                  },
                  "no_valid_recipients": {
                    "summary": "Nobody can be sent the message; nothing is created",
                    "value": {
                      "success": false,
                      "result_text": "Message not sent, there are no valid recipients",
                      "result_data": {
                        "summary": {
                          "total_selected": 2,
                          "emails_to_be_sent": 0,
                          "excluded_unsubscribed_contacts": 1,
                          "excluded_invalid_emails": 1,
                          "excluded_bounced_emails": 0,
                          "excluded_unverified_emails": 0
                        },
                        "recipients": [
                          {
                            "id": 7003,
                            "email": "cox@example.org",
                            "excluded_unsubscribed_contacts": true
                          },
                          {
                            "email": "kvrc-not-an-email",
                            "excluded_invalid_emails": true
                          }
                        ]
                      }
                    }
                  },
                  "invalid_json": {
                    "summary": "The body is not valid JSON",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid JSON"
                    }
                  },
                  "invalid_request": {
                    "summary": "The request could not be processed",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Invalid request"
                    }
                  },
                  "content_error": {
                    "summary": "The content could not be saved",
                    "value": {
                      "success": false,
                      "result_data": [],
                      "result_text": "Message content error"
                    }
                  },
                  "internal_error": {
                    "summary": "The message or its recipients could not be saved (no result_data key)",
                    "value": {
                      "success": false,
                      "result_text": "Internal error; please contact Envoke support"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V1SendUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V1SendForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V1SendTooManyRequests"
          }
        }
      }
    },
    "/v1/senders": {
      "get": {
        "summary": "List sender identities",
        "description": "Returns a paginated array of sender identities for the authenticated account. Supports filtering by id, name, email, and is_default. Note: sorting is not currently supported.",
        "operationId": "listSenders",
        "tags": [
          "Senders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1SendersLimit"
          },
          {
            "$ref": "#/components/parameters/V1SendersSkip"
          },
          {
            "$ref": "#/components/parameters/V1SendersFields"
          },
          {
            "$ref": "#/components/parameters/V1SendersResultType"
          },
          {
            "name": "filter[id]",
            "in": "query",
            "description": "Filter by sender ID. Supports operators: `(ne)`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[name]",
            "in": "query",
            "description": "Filter by sender display name. Supports operators: `(ne)`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[email]",
            "in": "query",
            "description": "Filter by sender email address. Supports operators: `(ne)`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "filter[is_default]",
            "in": "query",
            "description": "Filter by default status. Use 1 to return only the default sender, 0 to return only non-default senders.",
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of sender identities.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sender"
                      }
                    },
                    {
                      "type": "object",
                      "description": "Key-value map of sender ID to name.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  ]
                },
                "examples": {
                  "array": {
                    "summary": "Default array response",
                    "value": [
                      {
                        "id": "1",
                        "name": "City of Harborview",
                        "email": "communications@harborview.gov",
                        "is_default": true
                      },
                      {
                        "id": "2",
                        "name": "Parks & Recreation",
                        "email": "parks@harborview.gov",
                        "is_default": false
                      }
                    ]
                  },
                  "kvp": {
                    "summary": "Key-value pair response (result_type=kvp)",
                    "value": {
                      "1": "City of Harborview",
                      "2": "Parks & Recreation"
                    }
                  },
                  "empty": {
                    "summary": "No senders found",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a sender identity",
        "description": "Creates a new sender identity. Both name and email are required. The first identity created is automatically set as the account's default.",
        "operationId": "createSender",
        "tags": [
          "Senders"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SenderCreate"
              },
              "examples": {
                "basic": {
                  "summary": "Create a sender identity",
                  "value": {
                    "name": "City of Harborview",
                    "email": "communications@harborview.gov"
                  }
                },
                "department": {
                  "summary": "Create a department sender",
                  "value": {
                    "name": "Parks & Recreation",
                    "email": "parks@harborview.gov"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sender created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersCreateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Created",
                  "result_data": {
                    "id": "1",
                    "name": "City of Harborview",
                    "email": "communications@harborview.gov",
                    "is_default": true,
                    "dkim_status": {
                      "verified": true,
                      "domain": "harborview.gov"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersValidationErrorResponse"
                },
                "examples": {
                  "name_required": {
                    "summary": "Missing name",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The field 'name' is required"
                        ]
                      }
                    }
                  },
                  "email_required": {
                    "summary": "Missing email",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email is required"
                        ]
                      }
                    }
                  },
                  "invalid_email": {
                    "summary": "Invalid email format",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email address is not valid"
                        ]
                      }
                    }
                  },
                  "blocked_domain": {
                    "summary": "Free or personal email provider",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Third party email providers such as gmail.com, yahoo.com, etc. cannot be used as senders"
                        ]
                      }
                    }
                  },
                  "duplicate": {
                    "summary": "Duplicate name and email combination",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "A sender with this name and email already exists"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/senders/{id}": {
      "get": {
        "summary": "Get a single sender identity",
        "description": "Returns a single sender identity by ID. Returns an empty array ([]) if the ID is not found.",
        "operationId": "getSender",
        "tags": [
          "Senders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SenderId"
          },
          {
            "$ref": "#/components/parameters/V1SendersFields"
          }
        ],
        "responses": {
          "200": {
            "description": "The requested sender identity, or an empty array if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Sender"
                    },
                    {
                      "type": "array",
                      "maxItems": 0
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Sender found",
                    "value": {
                      "id": "1",
                      "name": "City of Harborview",
                      "email": "communications@harborview.gov",
                      "is_default": true
                    }
                  },
                  "not_found": {
                    "summary": "Sender not found",
                    "value": []
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a sender identity",
        "description": "Updates the name and/or email of an existing sender identity. At least one field must be provided. id and is_default are read-only and cannot be changed via PATCH — to change the default sender use POST /senders/{id}/default. If id is included in the request body it must match the {id} in the URL.",
        "operationId": "updateSender",
        "tags": [
          "Senders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SenderId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SenderUpdate"
              },
              "examples": {
                "rename": {
                  "summary": "Update the name only",
                  "value": {
                    "name": "Office of Communications"
                  }
                },
                "update_email": {
                  "summary": "Update the email only",
                  "value": {
                    "email": "info@harborview.gov"
                  }
                },
                "both": {
                  "summary": "Update both name and email",
                  "value": {
                    "name": "Office of Communications",
                    "email": "info@harborview.gov"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sender updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersUpdateSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": {
                    "id": "2",
                    "name": "Office of Communications",
                    "email": "info@harborview.gov",
                    "is_default": false,
                    "dkim_status": {
                      "verified": true,
                      "domain": "harborview.gov"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, sender not found, or id mismatch.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1SendersValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/V1SendersErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "not_found": {
                    "summary": "Sender not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "errors": [
                        "Item with id '42' not found"
                      ]
                    }
                  },
                  "id_mismatch": {
                    "summary": "id in body does not match URL id",
                    "value": {
                      "success": false,
                      "result_text": "Provided 'id': 2 does not match 'id' in request body (999)",
                      "result_data": []
                    }
                  },
                  "invalid_email": {
                    "summary": "Invalid email format",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Email address is not valid"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a sender identity",
        "description": "Permanently deletes a sender identity. The default sender cannot be deleted — use POST /senders/{id}/default to promote a different identity first.",
        "operationId": "deleteSender",
        "tags": [
          "Senders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SenderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Sender deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersDeleteSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Deleted",
                  "result_data": []
                }
              }
            }
          },
          "400": {
            "description": "Sender not found or sender is the default.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1SendersValidationErrorResponse"
                },
                "examples": {
                  "not_found": {
                    "summary": "Sender not found",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "errors": [
                        "Item with id '42' not found"
                      ]
                    }
                  },
                  "is_default": {
                    "summary": "Cannot delete the default sender",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "The default sender identity cannot be deleted"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/senders/{id}/default": {
      "post": {
        "summary": "Set the default sender",
        "description": "Promotes the specified sender identity to be the account's default. Clears the default flag from all other identities and syncs the account's default From name and email. The default sender is used automatically when no sender is explicitly specified in a campaign.",
        "operationId": "setDefaultSender",
        "tags": [
          "Senders"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SenderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Default sender updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetDefaultSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Updated",
                  "result_data": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/V1SendersNotFoundMutate"
          }
        }
      }
    },
    "/v2/reports/messages": {
      "get": {
        "tags": [
          "Message activity"
        ],
        "operationId": "listMessageActivity",
        "summary": "List activity metrics per message",
        "description": "One row per message with the ten metrics as integers, newest sent_time first then highest id (fixed order). Includes sent campaigns and automated messages; `type` narrows, `include_in_progress=1` adds campaigns still sending; drafts, scheduled, Transactional-status, and deleted or hidden messages never appear (an automated message whose sequence is transactional is included). A message is listed when it matches every filter, with zeros when it has no activity; with an activity window, messages launched after `activity_to` are left out. A/B tests are one row with `has_variants: true` and, for a subject-line test, `subject: null`. Filters combine with AND. Unknown parameters give `Parameter '{name}' is not valid`.",
        "parameters": [
          {
            "name": "message_id",
            "in": "query",
            "description": "One message ID (`message_id=2001`) or several (`message_id[]=2001&message_id[]=2004`). Not a whole number: `Invalid 'message_id': '{value}'`.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  }
                }
              ]
            },
            "example": 2001
          },
          {
            "name": "group_id",
            "in": "query",
            "description": "One message folder ID or several (`group_id[]=`). Messages in the folder or in any of its subfolders, at any depth, are included (the beta's message_group_id took direct members only).",
            "schema": {
              "oneOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  }
                }
              ]
            },
            "example": 31
          },
          {
            "name": "tag_id",
            "in": "query",
            "description": "Only messages whose recipient_tag is this tag. Takes the tag's ID (from `GET /v2/tags`), not the name the row shows. One value (`Parameter 'tag_id' accepts one value`).",
            "schema": {
              "type": "integer"
            },
            "example": 512
          },
          {
            "name": "segment_id",
            "in": "query",
            "description": "Only messages whose recipient_segment is this segment. Takes the segment's ID (from the segments in the Envoke app), not the name the row shows. One value.",
            "schema": {
              "type": "integer"
            },
            "example": 77
          },
          {
            "name": "subscription_id",
            "in": "query",
            "description": "Only messages whose recipient_subscription is this subscription. Takes the subscription's ID (from `GET /v2/subscriptions`), not the name the row shows. One value.",
            "schema": {
              "type": "integer"
            },
            "example": 514
          },
          {
            "name": "type",
            "in": "query",
            "description": "`campaign` or `automated`; both by default. Otherwise `Invalid 'type': '{value}' (possible values: 'campaign', 'automated')`.",
            "schema": {
              "type": "string",
              "enum": [
                "campaign",
                "automated"
              ]
            },
            "example": "automated"
          },
          {
            "name": "include_in_progress",
            "in": "query",
            "description": "`1` adds campaigns still sending (AB test in progress, Queuing, Sending); their numbers change until the send completes. Otherwise `Invalid 'include_in_progress': '{value}' (possible values: '0', '1')`.",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ],
              "default": 0
            },
            "example": 1
          },
          {
            "$ref": "#/components/parameters/SentFrom"
          },
          {
            "$ref": "#/components/parameters/SentTo"
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          },
          {
            "$ref": "#/components/parameters/V2ReportsLimit"
          },
          {
            "$ref": "#/components/parameters/V2ReportsSkip"
          }
        ],
        "responses": {
          "200": {
            "description": "Rows, or `[]` when nothing matches.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MessageActivityRow"
                  }
                },
                "examples": {
                  "array": {
                    "summary": "One row",
                    "value": [
                      {
                        "id": "2001",
                        "name": "Spring Season Announcement",
                        "subject": "Our spring season is here, {fname}",
                        "type": "campaign",
                        "sent_time": "2026-04-15 10:00:00",
                        "recipient_subscription": "Season News",
                        "recipient_tag": "",
                        "recipient_segment": "",
                        "has_variants": false,
                        "sends": 412,
                        "delivered": 403,
                        "bounces": 9,
                        "hard_bounces": 5,
                        "soft_bounces": 4,
                        "opens": 188,
                        "clicks": 57,
                        "unsubscribes": 4,
                        "spam_complaints": 1,
                        "skips": 6
                      }
                    ]
                  },
                  "empty": {
                    "summary": "Nothing matches",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2ReportsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v2/reports/messages/{id}": {
      "get": {
        "tags": [
          "Message stats"
        ],
        "operationId": "getMessageStats",
        "summary": "Get the full report for one message",
        "description": "Totals, rates, bounce and preference-change breakdowns, links, email clients, unsubscribe reasons, polls and A/B variants for one message. Accepts only `activity_from` and `activity_to`; with either, `unsubscribe_reasons` and `polls` are `null` because they describe contacts as they are now. `variants` is always included when `has_variants` is true; no parameter requests or suppresses it. It is `[]` when the message's A/B block no longer exists. A message that does not exist, belongs to another account, is deleted, or has not started sending returns `[]`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/V2ReportsMessageId"
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          }
        ],
        "responses": {
          "200": {
            "description": "The report, or `[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessageStats"
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResult"
                    }
                  ]
                },
                "examples": {
                  "report": {
                    "summary": "A message",
                    "value": {
                      "id": "2001",
                      "name": "Spring Season Announcement",
                      "subject": "Our spring season is here, {fname}",
                      "type": "campaign",
                      "sent_time": "2026-04-15 10:00:00",
                      "has_variants": false,
                      "totals": {
                        "sends": 412,
                        "delivered": 403,
                        "bounces": 9,
                        "hard_bounces": 5,
                        "soft_bounces": 4,
                        "opens": 188,
                        "clicks": 57,
                        "unsubscribes": 4,
                        "spam_complaints": 1,
                        "skips": 6
                      },
                      "rates": {
                        "delivered": 0.9782,
                        "open": 0.4665,
                        "click": 0.1414,
                        "click_to_open": 0.3032,
                        "bounce": 0.0218,
                        "unsubscribe": 0.0097
                      },
                      "bounces": [
                        {
                          "type": "hard_bounce",
                          "label": "Hard bounce",
                          "contacts": 3
                        },
                        {
                          "type": "soft_bounce",
                          "label": "Soft bounce",
                          "contacts": 2
                        },
                        {
                          "type": "deferred",
                          "label": "Temporarily deferred",
                          "contacts": 1
                        },
                        {
                          "type": "rejected",
                          "label": "Rejected",
                          "contacts": 0
                        },
                        {
                          "type": "invalid_address",
                          "label": "Invalid email address",
                          "contacts": 2
                        },
                        {
                          "type": "other",
                          "label": "Other",
                          "contacts": 1
                        }
                      ],
                      "preference_changes": [
                        {
                          "type": "revoked_consent",
                          "label": "Revoked consent",
                          "contacts": 3
                        },
                        {
                          "type": "one_click_unsubscribe",
                          "label": "One-click unsubscribe",
                          "contacts": 2
                        },
                        {
                          "type": "spam_reported",
                          "label": "Spam reported",
                          "contacts": 1
                        },
                        {
                          "type": "subscription_changed",
                          "label": "Subscription changed",
                          "contacts": 2
                        },
                        {
                          "type": "language_changed",
                          "label": "Language changed",
                          "contacts": 1
                        },
                        {
                          "type": "express_consent_granted",
                          "label": "Express consent granted",
                          "contacts": 0
                        }
                      ],
                      "links": [
                        {
                          "id": "88121",
                          "url": "https://example.org/season",
                          "title": "See the full season",
                          "type": "link",
                          "clicks": 41
                        },
                        {
                          "id": "88122",
                          "url": "https://example.org/tickets/season-pass",
                          "title": "Buy a season pass",
                          "type": "link",
                          "clicks": 22
                        },
                        {
                          "id": "88124",
                          "url": "",
                          "title": "Envoke Preferences / Unsubscribe",
                          "type": "pref",
                          "clicks": 6
                        },
                        {
                          "id": "88123",
                          "url": "",
                          "title": "Envoke View Message in Browser",
                          "type": "view",
                          "clicks": 4
                        }
                      ],
                      "email_clients": {
                        "known_opens": 196,
                        "machine_open_records": 52,
                        "by_app": [
                          {
                            "name": "Apple Mail",
                            "opens": 74
                          },
                          {
                            "name": "Gmail",
                            "opens": 61
                          },
                          {
                            "name": "Outlook",
                            "opens": 38
                          },
                          {
                            "name": "Yahoo",
                            "opens": 6
                          },
                          {
                            "name": "Other",
                            "opens": 17
                          }
                        ]
                      },
                      "unsubscribe_reasons": [
                        {
                          "reason": "no_longer_interested",
                          "contacts": 1
                        },
                        {
                          "reason": "too_many_emails",
                          "contacts": 2
                        }
                      ],
                      "polls": [],
                      "variants": []
                    }
                  },
                  "empty": {
                    "summary": "Not found",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2ReportsForbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v2/reports/messages/compare": {
      "get": {
        "tags": [
          "Message stats"
        ],
        "operationId": "compareMessageStats",
        "summary": "Compare several messages or a folder",
        "description": "Totals and rates per message plus a combined block whose totals are distinct contacts across the set. Give exactly one of `message_id` (one or more, at most 100; a repeated ID counts once) or `group_id` (one folder, subfolders included). With an activity window, messages launched after `activity_to` are left out of the set, as the list leaves them out; a `message_id` launched later is dropped without an error. Errors: `One of the parameters 'message_id' or 'group_id' must be provided`, `Only one of the parameters 'message_id' or 'group_id' may be provided`, `No message found for 'message_id': {id}` (one per unknown id, including messages in other accounts, deleted, or not sent), `No message group found for 'group_id': {id}`, `Too many messages to compare: {n} (maximum 100)`.",
        "parameters": [
          {
            "name": "message_id",
            "in": "query",
            "description": "One or more message IDs (`message_id[]=2001&message_id[]=2004`), each a sent campaign or automated message in the account. At most 100; a repeated ID counts once.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  },
                  "maxItems": 100
                }
              ]
            },
            "example": [
              2001,
              2004
            ]
          },
          {
            "name": "group_id",
            "in": "query",
            "description": "One message folder ID; every sent campaign and automated message filed in it or in any of its subfolders, at any depth.",
            "schema": {
              "type": "integer"
            },
            "example": 31
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          }
        ],
        "responses": {
          "200": {
            "description": "The comparison.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareResponse"
                },
                "examples": {
                  "two": {
                    "summary": "Two messages",
                    "value": {
                      "messages": [
                        {
                          "id": "2004",
                          "name": "Pirates Opening Night",
                          "sent_time": "2026-05-14 09:30:00",
                          "totals": {
                            "sends": 386,
                            "delivered": 379,
                            "bounces": 7,
                            "hard_bounces": 4,
                            "soft_bounces": 3,
                            "opens": 201,
                            "clicks": 64,
                            "unsubscribes": 2,
                            "spam_complaints": 0,
                            "skips": 3
                          },
                          "rates": {
                            "delivered": 0.9819,
                            "open": 0.5303,
                            "click": 0.1689,
                            "click_to_open": 0.3184,
                            "bounce": 0.0181,
                            "unsubscribe": 0.0052
                          }
                        },
                        {
                          "id": "2001",
                          "name": "Spring Season Announcement",
                          "sent_time": "2026-04-15 10:00:00",
                          "totals": {
                            "sends": 412,
                            "delivered": 403,
                            "bounces": 9,
                            "hard_bounces": 5,
                            "soft_bounces": 4,
                            "opens": 188,
                            "clicks": 57,
                            "unsubscribes": 4,
                            "spam_complaints": 1,
                            "skips": 6
                          },
                          "rates": {
                            "delivered": 0.9782,
                            "open": 0.4665,
                            "click": 0.1414,
                            "click_to_open": 0.3032,
                            "bounce": 0.0218,
                            "unsubscribe": 0.0097
                          }
                        }
                      ],
                      "combined": {
                        "message_count": 2,
                        "totals": {
                          "sends": 702,
                          "delivered": 686,
                          "bounces": 16,
                          "hard_bounces": 9,
                          "soft_bounces": 7,
                          "opens": 331,
                          "clicks": 108,
                          "unsubscribes": 6,
                          "spam_complaints": 1,
                          "skips": 9
                        },
                        "rates": {
                          "delivered": 0.9772,
                          "open": 0.4825,
                          "click": 0.1574,
                          "click_to_open": 0.3263,
                          "bounce": 0.0228,
                          "unsubscribe": 0.0085
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/V2ReportsForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v2/reports/messages/{id}/contacts": {
      "get": {
        "tags": [
          "Message contacts"
        ],
        "operationId": "listMessageContacts",
        "summary": "List the contacts behind one message's metric",
        "description": "One row per distinct contact with the event for the message: id, email, remote_id, event_time, count. Each event lists the contacts its metric counts, so the rows equal the single-message report's totals for the same window, less contacts deleted since; a message with an anonymous poll leaves every poll-answer click out of clicked. not_opened and not_clicked are the sent contacts minus the opened or clicked contacts, bounced contacts included (event_time null). Ordered by event_time descending then contact id descending (negative events by id descending). Answers for the same messages as the single-message report; a message that is not found returns []. Flat parameters, each taking one value except bounce_type, which accepts an array; any other parameter is a 400 `Parameter '{name}' is not valid`. On an account restricted from contact-level email activity, returns 403 for every event before checking parameters.",
        "parameters": [
          {
            "$ref": "#/components/parameters/V2ReportsMessageId"
          },
          {
            "$ref": "#/components/parameters/Event"
          },
          {
            "$ref": "#/components/parameters/LinkId"
          },
          {
            "$ref": "#/components/parameters/BounceType"
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          },
          {
            "$ref": "#/components/parameters/ContactFields"
          },
          {
            "$ref": "#/components/parameters/ContactsLimit"
          },
          {
            "$ref": "#/components/parameters/V2ReportsSkip"
          }
        ],
        "responses": {
          "200": {
            "description": "The rows, or `[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MessageContactRow"
                  }
                },
                "examples": {
                  "opened": {
                    "summary": "event=opened",
                    "value": [
                      {
                        "id": "1008",
                        "email": "p.nguyen@example.org",
                        "remote_id": "MRCT-0501",
                        "event_time": "2026-05-16 19:44:02",
                        "count": 2
                      },
                      {
                        "id": "1005",
                        "email": "l.ferreira@example.org",
                        "remote_id": "",
                        "event_time": "2026-05-15 07:12:55",
                        "count": 1
                      },
                      {
                        "id": "1004",
                        "email": "maya.chen@example.org",
                        "remote_id": "MRCT-0412",
                        "event_time": "2026-05-14 12:02:44",
                        "count": 1
                      }
                    ]
                  },
                  "ids": {
                    "summary": "event=not_opened&fields=id (sent, did not open)",
                    "value": [
                      {
                        "id": "1006"
                      },
                      {
                        "id": "1003"
                      },
                      {
                        "id": "1002"
                      }
                    ]
                  },
                  "empty": {
                    "summary": "Not found, or no contact has the event",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                },
                "example": {
                  "success": false,
                  "result_data": {
                    "errors": [
                      "Parameter 'event' must be provided",
                      "Invalid 'activity_from': '05/14/2026' (expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)"
                    ]
                  },
                  "result_text": "Validation failed, see 'result_data' -> 'errors'"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ContactForbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v2/reports/contacts/{id}": {
      "get": {
        "tags": [
          "Contact activity"
        ],
        "operationId": "getContactActivity",
        "summary": "Get one contact's email activity",
        "description": "The contact, a summary of message counts, and the messages the contact was sent (newest first, paged with limit and skip; summary is never paged). A contact that does not exist, belongs to another account, or is deleted returns `[]`. On an account restricted from contact-level email activity, returns 403 `Contact-level email activity is not available on this account`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/V2ReportsContactId"
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          },
          {
            "$ref": "#/components/parameters/V2ReportsLimit"
          },
          {
            "$ref": "#/components/parameters/V2ReportsSkip"
          }
        ],
        "responses": {
          "200": {
            "description": "The activity, or `[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ContactActivity"
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResult"
                    }
                  ]
                },
                "examples": {
                  "activity": {
                    "summary": "A contact",
                    "value": {
                      "contact": {
                        "id": "1004",
                        "email": "maya.chen@example.org",
                        "remote_id": "MRCT-0412"
                      },
                      "summary": {
                        "sends": 3,
                        "delivered": 3,
                        "bounces": 0,
                        "opens": 2,
                        "clicks": 1,
                        "unsubscribes": 0,
                        "last_send_time": "2026-05-14 09:31:07",
                        "last_open_time": "2026-05-14 12:02:44",
                        "last_click_time": "2026-05-14 12:03:10"
                      },
                      "messages": [
                        {
                          "id": "2004",
                          "name": "Pirates Opening Night",
                          "sent_time": "2026-05-14 09:31:07",
                          "delivered": true,
                          "bounced": false,
                          "opened": true,
                          "first_open_time": "2026-05-14 12:02:44",
                          "opens": 1,
                          "clicked": true,
                          "clicks": 1,
                          "links": [
                            {
                              "id": "88125",
                              "url": "https://example.org/tickets/pirates",
                              "first_click_time": "2026-05-14 12:03:10"
                            }
                          ],
                          "unsubscribed": false
                        }
                      ]
                    }
                  },
                  "empty": {
                    "summary": "Not found",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ContactForbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v2/reports/contacts": {
      "get": {
        "tags": [
          "Contact activity"
        ],
        "operationId": "findContactActivity",
        "summary": "Find a contact's email activity by email or remote ID",
        "description": "The same object as getContactActivity for the contact found by exactly one of `email` or `remote_id` (the whole value, compared as the Contact API's `filter[email]` and `filter[remote_id]` compare it; letter case does not matter). No match returns `[]`. Errors: `One of the parameters 'email' or 'remote_id' must be provided`, `Only one of the parameters 'email' or 'remote_id' may be provided`, `Invalid 'email': ''` (an empty value; also for `remote_id`), `More than one contact found for 'remote_id': '{value}'` (or `'email'`). On an account restricted from contact-level email activity, returns 403 `Contact-level email activity is not available on this account`.",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "description": "The contact's email address, URL-encoded. Not with remote_id.",
            "schema": {
              "type": "string"
            },
            "example": "maya.chen@example.org"
          },
          {
            "name": "remote_id",
            "in": "query",
            "description": "The contact's remote ID. Not with email. More than one matching contact is a 400.",
            "schema": {
              "type": "string"
            },
            "example": "MRCT-0412"
          },
          {
            "$ref": "#/components/parameters/ActivityFrom"
          },
          {
            "$ref": "#/components/parameters/ActivityTo"
          },
          {
            "$ref": "#/components/parameters/V2ReportsLimit"
          },
          {
            "$ref": "#/components/parameters/V2ReportsSkip"
          }
        ],
        "responses": {
          "200": {
            "description": "The activity, or `[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ContactActivity"
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResult"
                    }
                  ]
                },
                "examples": {
                  "activity": {
                    "summary": "Found by remote_id",
                    "value": {
                      "contact": {
                        "id": "1004",
                        "email": "maya.chen@example.org",
                        "remote_id": "MRCT-0412"
                      },
                      "summary": {
                        "sends": 3,
                        "delivered": 3,
                        "bounces": 0,
                        "opens": 2,
                        "clicks": 1,
                        "unsubscribes": 0,
                        "last_send_time": "2026-05-14 09:31:07",
                        "last_open_time": "2026-05-14 12:02:44",
                        "last_click_time": "2026-05-14 12:03:10"
                      },
                      "messages": [
                        {
                          "id": "2004",
                          "name": "Pirates Opening Night",
                          "sent_time": "2026-05-14 09:31:07",
                          "delivered": true,
                          "bounced": false,
                          "opened": true,
                          "first_open_time": "2026-05-14 12:02:44",
                          "opens": 1,
                          "clicked": true,
                          "clicks": 1,
                          "links": [
                            {
                              "id": "88125",
                              "url": "https://example.org/tickets/pirates",
                              "first_click_time": "2026-05-14 12:03:10"
                            }
                          ],
                          "unsubscribed": false
                        }
                      ]
                    }
                  },
                  "empty": {
                    "summary": "No match",
                    "value": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V2ReportsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/V2ReportsUnauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ContactForbidden"
          },
          "429": {
            "$ref": "#/components/responses/V2ReportsTooManyRequests"
          }
        }
      }
    },
    "/v1/hooks.json": {
      "get": {
        "summary": "List webhook subscriptions",
        "description": "Returns all webhook subscriptions for the authenticated account. All subscriptions are returned by default (no limit). Supports filtering by `type`, pagination, and field selection.",
        "operationId": "listHooks",
        "tags": [
          "Hooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/V1HooksLimit"
          },
          {
            "$ref": "#/components/parameters/V1HooksSkip"
          },
          {
            "$ref": "#/components/parameters/V1HooksFields"
          },
          {
            "$ref": "#/components/parameters/TypeFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "An array of webhook subscriptions. Returns `[]` if none exist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Hook"
                  }
                },
                "examples": {
                  "list": {
                    "summary": "Two subscriptions",
                    "value": [
                      {
                        "id": "9f1c2a42baedb56c7e65b3eeaf1a8814",
                        "type": "form_submission",
                        "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
                        "filter": {
                          "form_id": "abc123def456"
                        }
                      },
                      {
                        "id": "a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7",
                        "type": "email_open",
                        "url": "https://hooks.zapier.com/hooks/catch/123456/ghijkl/",
                        "filter": {
                          "message_id": "789"
                        }
                      }
                    ]
                  },
                  "filtered_by_type": {
                    "summary": "Filtered by type=form_submission",
                    "value": [
                      {
                        "id": "9f1c2a42baedb56c7e65b3eeaf1a8814",
                        "type": "form_submission",
                        "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
                        "filter": {
                          "form_id": "abc123def456"
                        }
                      }
                    ]
                  },
                  "empty": {
                    "summary": "No subscriptions",
                    "value": []
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a webhook subscription",
        "description": "Creates a new webhook subscription. `type` and `url` are required. `filter` is optional — omit it or pass `{}` to receive all events of the given type.\n\nReturns HTTP 201 on success. Returns HTTP 400 with a duplicate error if a subscription with the same `type` and `url` already exists.",
        "operationId": "createHook",
        "tags": [
          "Hooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HookCreate"
              },
              "examples": {
                "form_submission_with_filter": {
                  "summary": "Form submission — specific form (college admissions inquiry)",
                  "value": {
                    "type": "form_submission",
                    "url": "https://crm.ridgeline.edu/webhooks/envoke/admissions",
                    "filter": {
                      "form_id": "abc123def456"
                    }
                  }
                },
                "email_open_with_filter": {
                  "summary": "Email open — specific message by ID (fall registration reminder)",
                  "value": {
                    "type": "email_open",
                    "url": "https://portal.ridgeline.edu/webhooks/engagement",
                    "filter": {
                      "message_id": "789"
                    }
                  }
                },
                "email_click_with_filter": {
                  "summary": "Email click — specific message by external ID (alumni giving appeal)",
                  "value": {
                    "type": "email_click",
                    "url": "https://giving.ridgeline.edu/webhooks/envoke",
                    "filter": {
                      "message_external_id": "f5a3c1e9b7d5f3a1c9e7b5d3f1a9c7e5"
                    }
                  }
                },
                "email_bounce_hard_only": {
                  "summary": "Email bounce — hard bounces only (CRM address hygiene)",
                  "value": {
                    "type": "email_bounce",
                    "url": "https://crm.ridgeline.edu/webhooks/envoke/bounces",
                    "filter": {
                      "bounce_type": "hard"
                    }
                  }
                },
                "email_spam_report_no_filter": {
                  "summary": "Email spam report — all reports",
                  "value": {
                    "type": "email_spam_report",
                    "url": "https://crm.ridgeline.edu/webhooks/envoke/complaints",
                    "filter": {}
                  }
                },
                "new_contact_no_filter": {
                  "summary": "New contact — all new contacts (sync to student info system)",
                  "value": {
                    "type": "new_contact",
                    "url": "https://sis.ridgeline.edu/webhooks/contacts",
                    "filter": {}
                  }
                },
                "new_lead_no_filter": {
                  "summary": "New lead — all new leads (alert recruitment team)",
                  "value": {
                    "type": "new_lead",
                    "url": "https://crm.ridgeline.edu/webhooks/leads"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1HooksCreateSuccessResponse"
                },
                "example": {
                  "id": "9f1c2a42baedb56c7e65b3eeaf1a8814"
                }
              }
            }
          },
          "400": {
            "description": "Validation error or duplicate subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/V1HooksValidationErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/DuplicateErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missing_type": {
                    "summary": "Missing required field: type",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'type' is required"
                        ]
                      }
                    }
                  },
                  "invalid_type": {
                    "summary": "Unrecognised event type",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "Invalid webhook type: 'contact_created'"
                        ]
                      }
                    }
                  },
                  "missing_url": {
                    "summary": "Missing required field: url",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'url' is required"
                        ]
                      }
                    }
                  },
                  "invalid_url": {
                    "summary": "URL does not start with http:// or https://",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'url' must start with http:// or https://"
                        ]
                      }
                    }
                  },
                  "invalid_filter": {
                    "summary": "Filter value is not a scalar",
                    "value": {
                      "success": false,
                      "result_text": "Validation failed, see 'result_data' -> 'errors'",
                      "result_data": {
                        "errors": [
                          "'filter' values must be scalar (string, number, or boolean)"
                        ]
                      }
                    }
                  },
                  "duplicate": {
                    "summary": "Duplicate subscription",
                    "value": {
                      "success": false,
                      "result_text": "A webhook subscription with this type and URL already exists",
                      "result_data": {
                        "id": "9f1c2a42baedb56c7e65b3eeaf1a8814"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/hooks/{id}.json": {
      "get": {
        "summary": "Get a single webhook subscription",
        "description": "Returns a single webhook subscription by its ID. Returns `null` if the ID is not found.",
        "operationId": "getHook",
        "tags": [
          "Hooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/HookId"
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook subscription, or `null` if not found.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Hook"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "examples": {
                  "found": {
                    "summary": "Subscription found",
                    "value": {
                      "id": "9f1c2a42baedb56c7e65b3eeaf1a8814",
                      "type": "form_submission",
                      "url": "https://crm.ridgeline.edu/webhooks/envoke/admissions",
                      "filter": {
                        "form_id": "abc123def456"
                      }
                    }
                  },
                  "not_found": {
                    "summary": "Subscription not found",
                    "value": null
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook subscription",
        "description": "Permanently deletes a webhook subscription. No further events are delivered once deleted. The operation is idempotent — deleting a non-existent ID returns a success response.",
        "operationId": "deleteHook",
        "tags": [
          "Hooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/HookId"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription deleted (or did not exist — deletion is idempotent).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/V1HooksDeleteSuccessResponse"
                },
                "example": {
                  "success": true,
                  "result_text": "Deleted",
                  "result_data": []
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic Auth. Use your API ID as the username and API Key as the password. See https://developers.envoke.com/guides/authentication/."
      }
    },
    "parameters": {
      "V1ContactsContactId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The Envoke contact ID.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 1001
      },
      "V1ContactsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of contacts to return. Default 10, hard cap 100. When `fields` or `result_type=kvp` is also set, the default becomes 10,000 and a value above 100 is honoured as-is. `limit=1` returns a single contact as a bare object instead of an array (or `[]` when nothing matches). A value that is not a whole number gives `Invalid 'limit': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V1ContactsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of contacts to skip. Use with `limit` and `sort` to paginate. A value that is not a whole number gives `Invalid 'skip': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V1ContactsFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each result, e.g. `id,email`. On the list endpoint, setting it raises the default limit to 10,000 and lifts the 100 cap.",
        "schema": {
          "type": "string"
        },
        "example": "id,email,consent_status"
      },
      "V1ContactsResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive an object of contact ID to email address (`{ \"1001\": \"alto.lead@example.com\" }`) instead of an array. Raises the default limit to 10,000 and lifts the 100 cap. Has no effect when `limit=1`.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "KeyFieldCreate": {
        "name": "key_field",
        "in": "query",
        "description": "The field used to check whether the contact already exists: `email` (default) or `remote_id`. With `remote_id`, the body must include `remote_id`. Any other value gives `Invalid 'key_field' value: '{value}'`. The parameter is `key_field`; the misspelling `keyfield` is ignored without an error.",
        "schema": {
          "type": "string",
          "enum": [
            "email",
            "remote_id"
          ],
          "default": "email"
        }
      },
      "KeyFieldUpdate": {
        "name": "key_field",
        "in": "query",
        "description": "How `PATCH /contacts` with an object body finds the contact: `id` (looked up), `email`, or `remote_id`. When not set, a body `id` is used directly without a lookup, otherwise `email`. Ignored for list bodies and by `PATCH /contacts/{id}`. Any other value gives `Invalid 'key_field' value: '{value}'`.",
        "schema": {
          "type": "string",
          "enum": [
            "id",
            "email",
            "remote_id"
          ]
        }
      },
      "Upsert": {
        "name": "upsert",
        "in": "query",
        "description": "When `1`, `true`, `yes`, or `on`, an existing contact matched by `key_field` is updated instead of rejected as a duplicate. An empty value (`?upsert=`) is false. An upsert that updates ignores empty values (`write_empty_values` has no effect on POST, though a custom field value of `\"\"` still deletes that value), does not default `consent_status` to `Not Provided`, ignores `id` in the body, and appends warnings after `. `, giving a double period.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "WriteEmptyValues": {
        "name": "write_empty_values",
        "in": "query",
        "description": "When `1`, `true`, `yes`, or `on`, an empty string (`\"\"`) clears a text field. Otherwise `\"\"`, `0`, `\"0\"`, `false`, and `[]` are ignored; `null` is always ignored. A custom field value of `\"\"` deletes that value with or without this parameter. `email` cannot be cleared (`Email must not be empty`), and `country` and `language` cannot be cleared.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "TagId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the tag.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V2TagsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside `?fields=` or `?result_type=kvp`, the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2TagsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2TagsFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,group"
      },
      "V2TagsResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive results as a flat key-value object (`{ \"<id>\": \"<name>\" }`) instead of an array of objects. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "SubscriptionId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the subscription.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V2SubscriptionsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside `?fields=` or `?result_type=kvp`, the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2SubscriptionsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2SubscriptionsFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,group"
      },
      "V2SubscriptionsResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive results as a flat key-value object (`{ \"<id>\": \"<name>\" }`) instead of an array of objects. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "CustomFieldId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the custom field.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V2CustomfieldsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside `?fields=` or `?result_type=kvp` (shaped responses), the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2CustomfieldsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2CustomfieldsFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When omitted, all fields are returned. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,field_type"
      },
      "V2CustomfieldsResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive results as a flat key-value object (`{ \"<id>\": \"<name>\" }`) instead of an array of objects. Useful for populating dropdowns. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "GroupType": {
        "name": "type",
        "in": "path",
        "required": true,
        "description": "The item type whose groups are being managed.",
        "schema": {
          "type": "string",
          "enum": [
            "tags",
            "subscriptions",
            "messages",
            "templates"
          ]
        }
      },
      "GroupId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the group.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V2GroupsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside `?fields=` or `?result_type=kvp`, the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2GroupsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2GroupsFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name"
      },
      "V2GroupsResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive results as a flat key-value object (`{ \"<id>\": \"<name>\" }`) instead of an array of objects. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "TemplateId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the template.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V2TemplatesLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside `?fields=` or `?result_type=kvp`, the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2TemplatesSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2TemplatesFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When set, the default limit increases to 10,000 and the 100-record hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,status"
      },
      "V2TemplatesResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive results as a flat key-value object (`{ \"<id>\": \"<name>\" }`) instead of an array of objects. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "IncludeHTML": {
        "name": "include_html",
        "in": "query",
        "description": "Set to `1` to include the full HTML content in a single-template response. Has no effect on list requests.",
        "schema": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "default": 0
        }
      },
      "V2MessagesMessageId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The Envoke message ID.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 2001
      },
      "V2MessagesLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of messages to return. Default 10, hard cap 100. When `fields` or `result_type=kvp` is also set, the default becomes 10,000 and a value above 100 is honoured as-is. `limit=1` returns a single message as a bare object instead of an array (or `[]` when nothing matches), and `result_type=kvp` has no effect with it. A value that is not a whole number gives `Invalid 'limit': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V2MessagesSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of messages to skip. Use with `limit` and `sort` to paginate. A value that is not a whole number gives `Invalid 'skip': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V2MessagesFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each result, e.g. `id,name,status`. Use names from the Message schema. On the list endpoint, setting it raises the default limit to 10,000 and lifts the 100 cap. On a single GET with `include_html`, list `html` to keep the HTML.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,status,send_time"
      },
      "V2MessagesResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to `kvp` to receive an object of message ID to name (`{ \"2001\": \"Spring Season Announcement\" }`) instead of an array. Raises the default limit to 10,000 and lifts the 100 cap. Has no effect when `limit=1` or on a single GET.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "IncludeHtml": {
        "name": "include_html",
        "in": "query",
        "description": "Single GET only. `1` adds the message HTML as the last key, `html`. With `0` or without the parameter, the HTML is left out. When `fields` is also set, list `html` in it to keep the HTML. Ignored on the list endpoint, which never returns HTML.",
        "schema": {
          "type": "integer",
          "enum": [
            0,
            1
          ]
        },
        "example": 1
      },
      "SenderId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The unique ID of the sender identity.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V1SendersLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of records to return. Default is 10, hard cap is 100. When used alongside ?fields= or ?result_type=kvp, the cap is lifted and an explicit limit above 100 is honoured as-is.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 10
        }
      },
      "V1SendersSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V1SendersFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string"
        },
        "example": "id,name,email"
      },
      "V1SendersResultType": {
        "name": "result_type",
        "in": "query",
        "description": "Set to kvp to receive results as a flat key-value object ({ \"<id>\": \"<name>\" }) instead of an array of objects. When set, the default limit increases to 10,000 and the hard cap is lifted.",
        "schema": {
          "type": "string",
          "enum": [
            "kvp"
          ]
        }
      },
      "SentFrom": {
        "name": "sent_from",
        "in": "query",
        "description": "Only messages whose sent_time is on or after this value. `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` (URL-encode the space), read in the account's time zone (the zone the Message API uses); inclusive; a date alone covers the whole day; the year must be from 1970 to 2999. Any other value gives `Invalid 'sent_from': '{value}' (expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)`.",
        "schema": {
          "type": "string"
        },
        "example": "2026-04-01"
      },
      "SentTo": {
        "name": "sent_to",
        "in": "query",
        "description": "Only messages whose sent_time is on or before this value. `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` (URL-encode the space), read in the account's time zone (the zone the Message API uses); inclusive; a date alone covers the whole day; the year must be from 1970 to 2999. Any other value gives `Invalid 'sent_to': '{value}' (expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)`.",
        "schema": {
          "type": "string"
        },
        "example": "2026-04-30"
      },
      "ActivityFrom": {
        "name": "activity_from",
        "in": "query",
        "description": "Count only activity records made on or after this value. `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` (URL-encode the space), read in the account's time zone (the zone the Message API uses); inclusive; a date alone covers the whole day; the year must be from 1970 to 2999. Any other value gives `Invalid 'activity_from': '{value}' (expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)`.",
        "schema": {
          "type": "string"
        },
        "example": "2026-05-01"
      },
      "ActivityTo": {
        "name": "activity_to",
        "in": "query",
        "description": "Count only activity records made on or before this value. `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` (URL-encode the space), read in the account's time zone (the zone the Message API uses); inclusive; a date alone covers the whole day; the year must be from 1970 to 2999. Any other value gives `Invalid 'activity_to': '{value}' (expected YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)`.",
        "schema": {
          "type": "string"
        },
        "example": "2026-05-31 23:59:59"
      },
      "V2ReportsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of items. Default 10, hard cap 100 (larger values return 100); `0`, a value of zeros such as `00`, or empty gives the default; `limit=1` still returns an array. Not a whole number: `Invalid 'limit': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 10
        },
        "example": 25
      },
      "V2ReportsSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of items to skip. Not a whole number: `Invalid 'skip': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        },
        "example": 25
      },
      "V2ReportsMessageId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The Envoke message ID. A non-numeric value returns 404.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 2004
      },
      "V2ReportsContactId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The Envoke contact ID (Contact API `id`). A non-numeric value returns 404.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 1004
      },
      "Event": {
        "name": "event",
        "in": "query",
        "required": true,
        "description": "Which contacts to list. sent: type 1 (rows = totals.sends). delivered: sent with no bounce record for the message; in an activity window, sent inside the window with no bounce at any time (rows = totals.delivered). bounced: any type 2, or the bounce_type reasons (rows = totals.bounces). opened: type 3 (rows = totals.opens). clicked: type 4, or the link_id link (rows = totals.clicks, except that a message with an anonymous poll leaves out every poll-answer click). unsubscribed: 5/11 or 5/30 (rows = totals.unsubscribes). spam_reported: 5/12 or 5/13 (rows = totals.spam_complaints). skipped: type 6 (rows = totals.skips). not_opened and not_clicked: the sent contacts minus the opened (or clicked) contacts, bounced contacts included, as the segment conditions \"didn't open\" and \"didn't click\" (lifetime, not_opened is sent minus the sent contacts who opened; Envoke can record an open or click for a contact with no send record, who is in opened but not in sent or not_opened, so not_opened + opened can exceed sent). Contacts deleted since are not listed. Missing: `Parameter 'event' must be provided`; other values: `Invalid 'event': '{value}' (possible values: 'sent', 'delivered', 'bounced', 'opened', 'clicked', 'unsubscribed', 'spam_reported', 'skipped', 'not_opened', 'not_clicked')`.",
        "schema": {
          "type": "string",
          "enum": [
            "sent",
            "delivered",
            "bounced",
            "opened",
            "clicked",
            "unsubscribed",
            "spam_reported",
            "skipped",
            "not_opened",
            "not_clicked"
          ]
        },
        "example": "opened"
      },
      "LinkId": {
        "name": "link_id",
        "in": "query",
        "description": "With event=clicked, only contacts who clicked this link; with event=not_clicked, sent contacts who did not click it. One tracked link of the message (the id in the single-message report's links); poll answers are not accepted. Not a whole number: `Invalid 'link_id': '{value}'`; not a link of this message: `Invalid 'link_id': '{value}'` (checked only when the message is found); with another event: `Parameter 'link_id' requires event 'clicked' or 'not_clicked'`.",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 88125
      },
      "BounceType": {
        "name": "bounce_type",
        "in": "query",
        "description": "With event=bounced, only contacts with one of these bounce reasons, the type keys of the single-message report's bounces block. One value or several (`bounce_type[]=hard_bounce&bounce_type[]=invalid_address` lists the contacts of hard_bounces). Other values: `Invalid 'bounce_type': '{value}' (possible values: 'hard_bounce', 'soft_bounce', 'deferred', 'rejected', 'invalid_address', 'other')`; with another event: `Parameter 'bounce_type' requires event 'bounced'`.",
        "schema": {
          "oneOf": [
            {
              "type": "string",
              "enum": [
                "hard_bounce",
                "soft_bounce",
                "deferred",
                "rejected",
                "invalid_address",
                "other"
              ]
            },
            {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "hard_bounce",
                  "soft_bounce",
                  "deferred",
                  "rejected",
                  "invalid_address",
                  "other"
                ]
              }
            }
          ]
        },
        "example": "hard_bounce"
      },
      "ContactFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated row fields to return, from id, email, remote_id, event_time and count; returned in that order whatever the order given. Lifts the limit: with fields and no limit, up to 10,000 rows; a limit above 100 is honoured up to 10,000, larger values return 10,000. Applies to every event. Other names, or an empty value: `Invalid 'fields': '{name}' (possible values: 'id', 'email', 'remote_id', 'event_time', 'count')`.",
        "schema": {
          "type": "string"
        },
        "example": "id,email"
      },
      "ContactsLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of rows. Default 10, cap 100 (larger values return 100); with fields, default 10,000 and cap 10,000. `0` or empty gives the default (10,000 with fields); a value of several zeros such as `00` gives 10, with or without fields; `limit=1` still returns an array. Not a whole number: `Invalid 'limit': '{value}'`.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 10
        },
        "example": 100
      },
      "HookId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The 32-character hex ID of the webhook subscription.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f]{32}$"
        }
      },
      "V1HooksLimit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of subscriptions to return. When omitted, all subscriptions are returned.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "V1HooksSkip": {
        "name": "skip",
        "in": "query",
        "description": "Number of records to skip. Used with `limit` for pagination.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      },
      "V1HooksFields": {
        "name": "fields",
        "in": "query",
        "description": "Comma-separated list of fields to include in each response object.",
        "schema": {
          "type": "string"
        },
        "example": "id,type,url"
      },
      "TypeFilter": {
        "name": "type",
        "in": "query",
        "description": "Filter results to subscriptions matching the given event type.",
        "schema": {
          "$ref": "#/components/schemas/HookType"
        }
      }
    },
    "schemas": {
      "Contact": {
        "type": "object",
        "description": "A contact as returned by GET and by the create, upsert, and key-field update responses. Every field is present unless `fields` selects some. An unset value is `\"\"`, never `null` (a stored `\"0\"` is also returned as `\"\"`).",
        "required": [
          "id",
          "external_id",
          "date_created",
          "remote_id",
          "first_name",
          "last_name",
          "title",
          "email",
          "company",
          "phone",
          "address_1",
          "address_2",
          "city",
          "country",
          "province",
          "postal_code",
          "website",
          "language",
          "feedback",
          "consent_status",
          "consent_description",
          "consent_effective_date",
          "consent_expiry",
          "custom_fields",
          "interests",
          "autoresponders"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Envoke contact ID, assigned on creation. Returned as a string.",
            "examples": [
              "1001"
            ]
          },
          "external_id": {
            "type": "string",
            "readOnly": true,
            "description": "32-character identifier used in public-facing links such as preference pages. Not the same as `id`. Ignored on write.",
            "examples": [
              "9f2c4e7a1b3d5f6a8c0e2b4d6f8a1c3e"
            ]
          },
          "date_created": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "When the contact was created, W3C format in UTC. Ignored on write. Filterable with the range operators.",
            "examples": [
              "2026-10-05T14:03:11+00:00"
            ]
          },
          "remote_id": {
            "type": "string",
            "maxLength": 60,
            "description": "Your own identifier for the contact, e.g. a CRM or membership ID. Usable as the key field on create and update. Too long: `Remote ID must not exceed 60 characters`.",
            "examples": [
              "HCS-0002",
              ""
            ]
          },
          "first_name": {
            "type": "string",
            "maxLength": 50,
            "description": "First name. Too long: `First name must not exceed 50 characters`.",
            "examples": [
              "Maya"
            ]
          },
          "last_name": {
            "type": "string",
            "maxLength": 50,
            "description": "Last name. Too long: `Last name must not exceed 50 characters`.",
            "examples": [
              "Okafor"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 50,
            "description": "Job title or honorific. Too long: `Title must not exceed 50 characters`.",
            "examples": [
              "Section Lead"
            ]
          },
          "email": {
            "type": "string",
            "maxLength": 60,
            "description": "Email address. Required on create, even with `key_field=remote_id`. Can be changed on update but not cleared. The account's duplicate check (usually on `email`) rejects a value that belongs to another contact: `contact with email = {value} already exists`.",
            "examples": [
              "alto.lead@example.com"
            ]
          },
          "company": {
            "type": "string",
            "maxLength": 80,
            "description": "Company or organisation. Too long: `Company must not exceed 80 characters`.",
            "examples": [
              "Harbourfront Choral Society",
              ""
            ]
          },
          "phone": {
            "type": "string",
            "maxLength": 25,
            "description": "Phone number, free text with no format check. Too long: `Phone must not exceed 25 characters`.",
            "examples": [
              "416-555-0142"
            ]
          },
          "address_1": {
            "type": "string",
            "maxLength": 100,
            "description": "Street address, line 1. Too long: `Address must not exceed 100 characters`.",
            "examples": [
              "48 Harbour Lane"
            ]
          },
          "address_2": {
            "type": "string",
            "maxLength": 100,
            "description": "Street address, line 2. Too long: `Address 2 must not exceed 100 characters`.",
            "examples": [
              "Unit 5"
            ]
          },
          "city": {
            "type": "string",
            "maxLength": 50,
            "description": "City. Too long: `City must not exceed 50 characters`.",
            "examples": [
              "Toronto"
            ]
          },
          "country": {
            "type": "string",
            "description": "Returned as a 2-letter ISO code, or `\"\"`. Accepts an ISO code (`CA`) or a recognised country name (`Canada`, `France`, `U.S.A.`, `UK`). An unrecognised value is not saved and produces the warning `'{value}' is not a valid value for country; field will not be updated`. Decides which consent rules apply; a contact with no country uses the account's default consent country. Cannot be cleared.",
            "examples": [
              "CA",
              ""
            ]
          },
          "province": {
            "type": "string",
            "maxLength": 50,
            "description": "Province or state, free text. Too long: `Province must not exceed 50 characters`.",
            "examples": [
              "ON"
            ]
          },
          "postal_code": {
            "type": "string",
            "maxLength": 15,
            "description": "Postal or ZIP code. Too long: `Postal / zip must not exceed 15 characters`.",
            "examples": [
              "M5V 0Z9"
            ]
          },
          "website": {
            "type": "string",
            "maxLength": 255,
            "description": "Website URL, free text with no URL check. Too long: `Website must not exceed 255 characters`.",
            "examples": [
              "https://example.org",
              ""
            ]
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "fr",
              ""
            ],
            "description": "Preferred language, returned as `en`, `fr`, or `\"\"`. Accepts `en`, `fr`, `English`, or `French`, case-insensitive. Any other value is not saved and produces the warning `'{value}' is not a valid value for language; field will not be updated`. Cannot be cleared.",
            "examples": [
              "en"
            ]
          },
          "feedback": {
            "type": "string",
            "description": "Free-text notes. HTML tags are removed before the value is saved, without an error.",
            "examples": [
              "Happy to help with ushering at the winter concert.",
              ""
            ]
          },
          "consent_status": {
            "type": "string",
            "description": "Name of the contact's current consent type. Writable values: `Express`, `Implied - Inquiry`, `Implied - Transaction`, `Implied - No Expiry`, `Not Provided`, `Revoked`, `Envoke Spam Reported`. Read-only values set by Envoke: `Expired` (implied consent that has expired) and `External Spam Reported` (from a spam complaint). Older contacts can also carry the retired types `Implied - Existing Relationship`, `Legacy Subscribed`, `Legacy Confirmed`, or `Legacy Unknown`, which cannot be written. Defaults to `Not Provided` on create. A downgrade is recorded but not made current unless `consent_force_downgrade` is sent; changes away from a spam-reported type, and `Revoked` to `Not Provided`, are blocked. Neither is reported in the response.",
            "examples": [
              "Express"
            ]
          },
          "consent_description": {
            "type": "string",
            "maxLength": 500,
            "description": "How and when consent was obtained. Text beyond 500 characters is cut off without an error. Never required.",
            "examples": [
              "Signed up at the spring concert"
            ]
          },
          "consent_effective_date": {
            "type": "string",
            "format": "date-time",
            "description": "When the current consent took effect, W3C format in UTC. On write, a value without a time zone is read as UTC; a date with no time is read as midnight in Envoke's server time zone. Defaults to the time of the request. Must not be in the future (`consent_effective_date cannot be in the future.`); an unreadable value gives `Invalid date format for consent_effective_date`. Filterable with the range operators; not sortable.",
            "examples": [
              "2026-09-12T18:30:00+00:00"
            ]
          },
          "consent_expiry": {
            "type": "string",
            "readOnly": true,
            "description": "Expiry date of the current consent as `YYYY-MM-DD`, or `\"\"`. `Implied - Inquiry` expires 6 months, and `Implied - Transaction` 2 years, after the effective date. On GET it is `\"\"` when the consent does not expire, when the contact's country is not `CA` (no country uses the account's default consent country), or when the account ignores consent expiry; create, upsert, and `PATCH /contacts` responses are not blanked. Ignored on write. Filterable with the range operators as a plain date; not sortable.",
            "examples": [
              "",
              "2027-03-12"
            ]
          },
          "custom_fields": {
            "type": "object",
            "description": "Every custom field that has a value for the contact, keyed by the custom field's name exactly as defined in the account. `{}` when there are none. Lead-scope (conversion page) fields are not returned.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "Voice_Part": "Alto",
                "Season_Ticket_Number": "HCS-ST-0417"
              }
            ]
          },
          "interests": {
            "type": "array",
            "description": "Names of every tag, subscription, and mandatory consent item the contact has. `[]` when none.",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Volunteer",
                "Alumni",
                "Concert News"
              ]
            ]
          },
          "autoresponders": {
            "type": "array",
            "description": "Names of the nurture campaigns (autoresponders) the contact is on. `[]` when none.",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Welcome Sequence"
              ],
              []
            ]
          }
        }
      },
      "InterestsWrite": {
        "description": "Tags, subscriptions, and mandatory consent items to write. A list of names adds each one and removes nothing. An object maps a name (or the item's numeric ID) to a state: `\"Set\"` or `1` adds, `\"Unset\"` or `0` removes. Names match ignoring case and treating a space and an underscore as the same. Numeric states must be JSON numbers; a string such as `\"1\"` is read as a name. Unknown name: `Invalid 'interest': '{name}' (for a list of valid interests try the endpoint '/interests')`. Wrong type: `'interests' must be an object or array`. Adding a subscription does not check the contact's sending consent; a `Not Provided` contact is subscribed like any other.",
        "oneOf": [
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Volunteer",
                "Concert News"
              ]
            ]
          },
          {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "Set",
                    "Unset"
                  ]
                },
                {
                  "type": "integer",
                  "enum": [
                    1,
                    0
                  ]
                }
              ]
            },
            "examples": [
              {
                "Volunteer": "Set",
                "Alumni": "Unset"
              }
            ]
          }
        ]
      },
      "AutorespondersWrite": {
        "description": "Nurture campaigns to write. A list of names adds and queues each one. An object maps a name (or the campaign's numeric ID) to a state: `\"Set\"`, `1`, or `2` adds and queues; `\"Unset\"` or `0` removes; `\"Dequeue\"` or `-1` stops further messages but leaves the contact on the campaign. Unknown name: `Invalid 'autoresponder': '{name}' (for a list of valid autoresponders try the endpoint '/autoresponders')`. Wrong type: `'autoresponders' must be an object or array`. A campaign is not added (the contact is not put on it) when the contact's consent does not allow sending, unless the campaign is transactional (never for a spam-reported contact); this is not reported. Campaigns cannot be created through the API.",
        "oneOf": [
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Welcome Sequence"
              ]
            ]
          },
          {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "Set",
                    "Unset",
                    "Dequeue"
                  ]
                },
                {
                  "type": "integer",
                  "enum": [
                    2,
                    1,
                    0,
                    -1
                  ]
                }
              ]
            },
            "examples": [
              {
                "Welcome Sequence": "Dequeue"
              }
            ]
          }
        ]
      },
      "ContactWritable": {
        "type": "object",
        "description": "Writable contact fields shared by create and update. A key that is not a contact field gives `'{key}' is not a valid contact field`. `external_id`, `date_created`, and `consent_expiry` are read-only and silently ignored.",
        "properties": {
          "remote_id": {
            "type": "string",
            "maxLength": 60,
            "examples": [
              "HCS-0002"
            ]
          },
          "first_name": {
            "type": "string",
            "maxLength": 50,
            "examples": [
              "Maya"
            ]
          },
          "last_name": {
            "type": "string",
            "maxLength": 50,
            "examples": [
              "Okafor"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 50,
            "examples": [
              "Section Lead"
            ]
          },
          "email": {
            "type": "string",
            "maxLength": 60,
            "description": "Must be a JSON string. Errors: `Email is required`, `Email must be a string`, `Email invalid`, `Email must not exceed 60 characters`; on update, `Email must not be empty`.",
            "examples": [
              "alto.lead@example.com"
            ]
          },
          "company": {
            "type": "string",
            "maxLength": 80,
            "examples": [
              "Harbourfront Choral Society"
            ]
          },
          "phone": {
            "type": "string",
            "maxLength": 25,
            "examples": [
              "416-555-0142"
            ]
          },
          "address_1": {
            "type": "string",
            "maxLength": 100,
            "examples": [
              "48 Harbour Lane"
            ]
          },
          "address_2": {
            "type": "string",
            "maxLength": 100,
            "examples": [
              "Unit 5"
            ]
          },
          "city": {
            "type": "string",
            "maxLength": 50,
            "examples": [
              "Toronto"
            ]
          },
          "country": {
            "type": "string",
            "description": "ISO code or recognised country name. Unrecognised values produce a warning and are not saved.",
            "examples": [
              "Canada",
              "CA"
            ]
          },
          "province": {
            "type": "string",
            "maxLength": 50,
            "examples": [
              "ON"
            ]
          },
          "postal_code": {
            "type": "string",
            "maxLength": 15,
            "examples": [
              "M5V 0Z9"
            ]
          },
          "website": {
            "type": "string",
            "maxLength": 255,
            "examples": [
              "https://example.org"
            ]
          },
          "language": {
            "type": "string",
            "description": "`en`, `fr`, `English`, or `French`, case-insensitive. Other values produce a warning and are not saved.",
            "examples": [
              "English",
              "en"
            ]
          },
          "feedback": {
            "type": "string",
            "description": "HTML is stripped.",
            "examples": [
              "Happy to help with ushering at the winter concert."
            ]
          },
          "consent_status": {
            "type": "string",
            "enum": [
              "Express",
              "Implied - Inquiry",
              "Implied - Transaction",
              "Implied - No Expiry",
              "Not Provided",
              "Revoked",
              "Envoke Spam Reported"
            ],
            "description": "Case-insensitive; a space and an underscore are treated as the same character. An unknown value gives `Invalid 'consent_status': '{value}' (possible values: 'Express', 'Implied - Inquiry', 'Implied - Transaction', 'Implied - No Expiry', 'Not Provided', 'Revoked', 'Envoke Spam Reported')`. Implied types do not apply to contacts in the EU consent region; a sentence is appended to `result_text` instead.",
            "examples": [
              "Express"
            ]
          },
          "consent_description": {
            "type": "string",
            "maxLength": 500,
            "examples": [
              "Signed up at the spring concert"
            ]
          },
          "consent_effective_date": {
            "type": "string",
            "description": "Date and time, e.g. `2026-09-12T18:30:00Z` or `2026-09-12 18:30:00` (UTC). Not in the future.",
            "examples": [
              "2026-09-12T18:30:00Z"
            ]
          },
          "consent_force_downgrade": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "writeOnly": true,
            "description": "Sent with `consent_status` to make a preempted downgrade current. True for `true`, `\"true\"`, `1`, `\"1\"`, `\"yes\"`, `\"on\"`, or `\"Set\"`; anything else is false. Does nothing on its own, on a new contact, or for blocked changes. Never returned.",
            "examples": [
              true
            ]
          },
          "custom_fields": {
            "type": "object",
            "description": "Values keyed by custom field name; matching ignores case and treats a space and an underscore as the same. IDs are not accepted as keys. Values are stored as text and not checked against the field's type. `\"\"` deletes the value; a value that cannot be stored (e.g. `null`) is skipped without an error. Fields not included are left unchanged. Unknown key: `Invalid 'custom_field': '{name}' (for a list of valid custom fields try the endpoint '/customfields')`. Not an object: `'custom_fields' must be an object`.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "Voice_Part": "Alto",
                "Season_Ticket_Number": "HCS-ST-0417"
              }
            ]
          },
          "interests": {
            "$ref": "#/components/schemas/InterestsWrite"
          },
          "autoresponders": {
            "$ref": "#/components/schemas/AutorespondersWrite"
          }
        }
      },
      "ContactCreate": {
        "description": "Request body for `POST /contacts`: a single JSON object. `email` is required; `remote_id` is also required with `key_field=remote_id`. `id` is ignored. A missing `consent_status` becomes `Not Provided` on a new contact.",
        "allOf": [
          {
            "$ref": "#/components/schemas/ContactWritable"
          },
          {
            "type": "object",
            "required": [
              "email"
            ]
          }
        ]
      },
      "ContactUpdate": {
        "description": "Request body for one contact in a PATCH. Send only the fields to change; only fields that differ from the stored value are written, and a request that changes nothing still succeeds. `id` identifies the contact and is never written.",
        "allOf": [
          {
            "$ref": "#/components/schemas/ContactWritable"
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "integer",
                  "string"
                ],
                "description": "On `PATCH /contacts/{id}`, must equal the URL id if sent (`\"1001\"` and `1001` count as the same). On `PATCH /contacts` without `key_field`, and in a list body, used directly without a lookup.",
                "examples": [
                  1001
                ]
              }
            }
          }
        ]
      },
      "V1ContactsCreateSuccessResponse": {
        "type": "object",
        "description": "Returned by a create, and by an upsert whether it creates or updates. `result_data` is the full contact. Warnings are appended to `result_text`: after a space, joined with `. `, on create; after `. ` (a double period) when an upsert updates. For an EU contact given an implied type, a consent sentence is appended in the same way.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "examples": [
              "The contact was successfully created.",
              "The contact was successfully updated."
            ]
          },
          "result_data": {
            "$ref": "#/components/schemas/Contact"
          }
        }
      },
      "UpdateByIdSuccessResponse": {
        "type": "object",
        "description": "Returned by `PATCH /contacts/{id}`. A short result, not the full contact.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "description": "`The contact was successfully updated.`, followed by a consent sentence when an EU rule applied.",
            "examples": [
              "The contact was successfully updated."
            ]
          },
          "result_data": {
            "type": "object",
            "required": [
              "id",
              "submit_type",
              "warnings"
            ],
            "properties": {
              "id": {
                "type": "integer",
                "description": "The contact ID, as an integer on this endpoint.",
                "examples": [
                  1001
                ]
              },
              "submit_type": {
                "type": "string",
                "const": "update"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "`country` and `language` warnings; `[]` when none.",
                "examples": [
                  [],
                  [
                    "'Narnia' is not a valid value for country; field will not be updated"
                  ]
                ]
              },
              "no_affected_rows": {
                "type": "boolean",
                "const": true,
                "description": "Present when no profile column changed (only custom fields, tags, nurture campaigns, or consent, or nothing at all)."
              },
              "inapplicable_consent_updated": {
                "type": "boolean",
                "const": true,
                "description": "Present when a change of `country` to an EU country reset an implied consent type to `Not Provided`."
              },
              "consent_fix_message": {
                "type": "string",
                "description": "Present with `inapplicable_consent_updated`; the sentence also appended to `result_text`.",
                "examples": [
                  " Their consent status was updated because Implied - Inquiry consent does not apply to contacts in the EU."
                ]
              }
            }
          }
        }
      },
      "UpdateByKeySuccessResponse": {
        "type": "object",
        "description": "Returned by `PATCH /contacts` with an object body. `result_data` is the full, updated contact. Warnings are appended to `result_text` after `. `, which follows the success message's own period.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "examples": [
              "The contact was successfully updated.",
              "The contact was successfully updated.. 'Narnia' is not a valid value for country; field will not be updated"
            ]
          },
          "result_data": {
            "$ref": "#/components/schemas/Contact"
          }
        }
      },
      "BulkUpdateResponse": {
        "type": "object",
        "description": "Returned by `PATCH /contacts` (or `PATCH /contacts/{id}`, whose id is then ignored) with a list body. HTTP 200 when every contact was updated; HTTP 400 with `success: false` when any failed, even though the others were updated.",
        "required": [
          "success",
          "successes",
          "failures",
          "result_data",
          "result_text"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "successes": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "integer"
              ]
            },
            "description": "IDs of the contacts that were updated.",
            "examples": [
              [
                "1006",
                "1007"
              ]
            ]
          },
          "failures": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "integer"
              ]
            },
            "description": "IDs of contacts that were found but failed, and `CONTACT_OBJECT_{n}` (zero-based list position) for contacts that could not be found.",
            "examples": [
              [
                "CONTACT_OBJECT_2"
              ]
            ]
          },
          "result_data": {
            "type": "object",
            "description": "Each contact's result keyed by contact ID or `CONTACT_OBJECT_{n}`. A found contact has the `result_data` of `PATCH /contacts/{id}` (or its `errors`, or `fields` and `warnings`); a contact that could not be found has `errors` with the lookup error.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "submit_type": {
                  "type": "string"
                },
                "warnings": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "fields": {
                  "type": [
                    "object",
                    "array"
                  ]
                }
              }
            }
          },
          "result_text": {
            "type": "string",
            "examples": [
              "All contacts were successfully updated",
              "2 contacts were successfully updated; 1 contacts were not updated"
            ]
          }
        }
      },
      "V1ContactsDeleteResponse": {
        "type": "object",
        "description": "Returned by DELETE, on success and on failure. There is no `result_data` key.",
        "required": [
          "success",
          "result_text"
        ],
        "additionalProperties": false,
        "properties": {
          "success": {
            "type": "boolean"
          },
          "result_text": {
            "type": "string",
            "examples": [
              "1 contact deleted",
              "1 contact not deleted, 1 - no matching contact found",
              "1 contact not deleted, 1 - can't delete due to consent status"
            ]
          }
        }
      },
      "V1ContactsValidationErrorResponse": {
        "type": "object",
        "description": "Body, query, and not-found-by-id errors. Every problem found by the check is listed.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FieldValidationErrorResponse": {
        "type": "object",
        "description": "Field lengths, email format, and the account's duplicate check, which run after the body checks. `result_data.fields` is keyed by database column (`first_name` is `fname`, `last_name` `lname`, `address_1` `addr1`, `address_2` `addr2`, `postal_code` `postal`, `external_id` `eid`; others match the API name), and `result_text` holds the messages joined with `, `. A duplicate-check failure has `fields: []`. On update, the envelope also carries `warnings`.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "examples": [
              "Email invalid, First name must not exceed 50 characters",
              "contact with email = alto.lead@example.com already exists"
            ]
          },
          "result_data": {
            "type": "object",
            "required": [
              "fields"
            ],
            "properties": {
              "fields": {
                "type": [
                  "object",
                  "array"
                ],
                "additionalProperties": {
                  "type": "string"
                },
                "examples": [
                  {
                    "email": "Email invalid",
                    "fname": "First name must not exceed 50 characters"
                  },
                  []
                ]
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "V1ContactsPlainErrorResponse": {
        "type": "object",
        "description": "Request-shape and lookup errors: the message is in `result_text` and `result_data` is an empty array.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "examples": [
              "Invalid request",
              "Invalid JSON",
              "Contact 'id': 1001 does not match 'id' in request body (1002)",
              "No contact was found with the 'email': 'nobody@example.com'"
            ]
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "Tag": {
        "type": "object",
        "description": "A tag (interest) definition.",
        "required": [
          "id",
          "external_id",
          "name",
          "group",
          "description",
          "display_names"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "42"
            ]
          },
          "external_id": {
            "type": "string",
            "readOnly": true,
            "description": "Externally visible identifier used in public-facing integrations.",
            "examples": [
              "a1b2c3d4e5f6"
            ]
          },
          "name": {
            "type": "string",
            "description": "The tag name.",
            "examples": [
              "Newsletter Subscribers"
            ]
          },
          "group": {
            "type": "string",
            "description": "Name of the group the tag belongs to. Empty string (`\"\"`) when no group is assigned.",
            "examples": [
              "Member Programs",
              ""
            ]
          },
          "description": {
            "type": "string",
            "description": "Internal description of the tag. Not visible to contacts. Empty string when not set.",
            "examples": [
              "Members interested in the annual conference"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code. Used in public-facing contexts such as website forms. Values are empty strings when no translation is set.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Newsletter",
                "fr": "Infolettre"
              }
            ]
          }
        }
      },
      "TagCreate": {
        "type": "object",
        "description": "Request body for creating a new tag. `name` is required.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The tag name.",
            "examples": [
              "Newsletter Subscribers"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the tag to. Accepts the group name (string) or group ID (integer). The group must be of type `interest`.",
            "examples": [
              "Member Programs",
              5
            ]
          },
          "description": {
            "type": "string",
            "description": "Internal description of the tag.",
            "examples": [
              "Members interested in the annual conference"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Newsletter",
                "fr": "Infolettre"
              }
            ]
          }
        }
      },
      "TagUpdate": {
        "type": "object",
        "description": "Request body for updating a tag. At least one field must be provided. `external_id` is silently ignored if included. If `id` is included, it must match the `{id}` in the URL.",
        "minProperties": 1,
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Optional. If provided, must match the `{id}` in the URL. A mismatch returns a validation envelope.",
            "examples": [
              42
            ]
          },
          "name": {
            "type": "string",
            "description": "New name for the tag.",
            "examples": [
              "Updated Tag Name"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the tag to. Accepts group name or group ID. Pass `\"\"` (empty string) to remove the group assignment.",
            "examples": [
              "Certification Programs",
              ""
            ]
          },
          "description": {
            "type": "string",
            "description": "Updated internal description.",
            "examples": [
              "Updated description"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Organic",
                "fr": "Biologique"
              }
            ]
          }
        }
      },
      "ContactsRequest": {
        "type": "object",
        "description": "Request body for adding or removing contacts. Provide exactly one of `contact_ids` or `contact_emails`.",
        "oneOf": [
          {
            "required": [
              "contact_ids"
            ],
            "properties": {
              "contact_ids": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "minItems": 1,
                "maxItems": 1000,
                "description": "Envoke contact IDs to add or remove.",
                "examples": [
                  [
                    101,
                    102,
                    103
                  ]
                ]
              }
            }
          },
          {
            "required": [
              "contact_emails"
            ],
            "properties": {
              "contact_emails": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "maxItems": 1000,
                "description": "Contact email addresses to add or remove.",
                "examples": [
                  [
                    "alice@example.com",
                    "bob@example.com"
                  ]
                ]
              }
            }
          }
        ]
      },
      "ContactsSuccessResponse": {
        "type": "object",
        "description": "Returned after a contacts add or remove operation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` if no found contacts failed to update. `true` even when some identifiers were not found."
          },
          "result_text": {
            "type": "string",
            "description": "Human-readable summary.",
            "examples": [
              "Set to contacts",
              "Set to contacts. Some contacts not found",
              "Error setting to contacts"
            ]
          },
          "result_data": {
            "type": "object",
            "required": [
              "processed"
            ],
            "properties": {
              "processed": {
                "type": "integer",
                "description": "Total number of input identifiers submitted."
              },
              "not_found": {
                "type": "array",
                "description": "IDs or emails that were not matched to any contact. Only present when at least one was not found.",
                "items": {
                  "type": [
                    "string",
                    "integer"
                  ]
                }
              },
              "failed": {
                "type": "array",
                "description": "Contacts that were found but failed during the update. Only present when failures occurred.",
                "items": {
                  "type": "object",
                  "required": [
                    "contact_id",
                    "reason"
                  ],
                  "properties": {
                    "contact_id": {
                      "type": [
                        "string",
                        "integer"
                      ]
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "V2TagsCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful tag creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/Tag"
          }
        }
      },
      "V2TagsUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful tag update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/Tag"
          }
        }
      },
      "V2TagsDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful tag deletion.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V2TagsValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body or path parameter validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "Subscription": {
        "type": "object",
        "description": "A subscription definition.",
        "required": [
          "id",
          "external_id",
          "name",
          "group",
          "prefpage_access",
          "display_names",
          "display_descriptions"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "12"
            ]
          },
          "external_id": {
            "type": "string",
            "readOnly": true,
            "description": "Externally visible identifier used in public-facing integrations.",
            "examples": [
              "a1b2c3d4e5f6"
            ]
          },
          "name": {
            "type": "string",
            "description": "The subscription name.",
            "examples": [
              "Campus Weekly Newsletter"
            ]
          },
          "group": {
            "type": "string",
            "description": "Name of the group the subscription belongs to. Empty string (`\"\"`) when no group is assigned.",
            "examples": [
              "Student Communications",
              ""
            ]
          },
          "prefpage_access": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "Controls whether this subscription appears on the public preference page. `\"public\"` (default) means it is visible to contacts. `\"private\"` hides it from public preference pages — requires the private subscriptions feature.",
            "examples": [
              "public"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code. Shown to contacts on the preference page. Values are empty strings when no translation is set. All enabled account languages are included as keys.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Campus Weekly Newsletter",
                "fr": "Bulletin hebdomadaire du campus"
              }
            ]
          },
          "display_descriptions": {
            "type": "object",
            "description": "Translated descriptions keyed by ISO language code. Shown alongside the display name on the preference page. Values are empty strings when no translation is set. All enabled account languages are included as keys.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "A weekly roundup of campus news and events.",
                "fr": "Un résumé hebdomadaire des nouvelles et événements du campus."
              }
            ]
          }
        }
      },
      "SubscriptionCreate": {
        "type": "object",
        "description": "Request body for creating a new subscription. `name` is required.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The subscription name.",
            "examples": [
              "Campus Weekly Newsletter"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the subscription to. Accepts the group name (string) or group ID (integer). The group must be of type `subscription`.",
            "examples": [
              "Student Communications",
              5
            ]
          },
          "prefpage_access": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "Preference page visibility. Defaults to `\"public\"`. Setting to `\"private\"` requires the private subscriptions feature.",
            "examples": [
              "public"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Campus Weekly Newsletter",
                "fr": "Bulletin hebdomadaire du campus"
              }
            ]
          },
          "display_descriptions": {
            "type": "object",
            "description": "Translated descriptions keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "A weekly roundup of campus news and events."
              }
            ]
          }
        }
      },
      "SubscriptionUpdate": {
        "type": "object",
        "description": "Request body for updating a subscription. At least one field must be provided. `external_id` is silently ignored if included. If `id` is included, it must match the `{id}` in the URL — if they differ, a validation envelope is returned.",
        "minProperties": 1,
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Optional. If provided, must match the `{id}` in the URL. A mismatch returns a validation envelope.",
            "examples": [
              12
            ]
          },
          "name": {
            "type": "string",
            "description": "New name for the subscription.",
            "examples": [
              "Updated Subscription Name"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the subscription to. Accepts group name or group ID. Pass `\"\"` (empty string) to remove the group assignment.",
            "examples": [
              "Alumni Relations",
              ""
            ]
          },
          "prefpage_access": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "Preference page visibility. Setting to `\"private\"` requires the private subscriptions feature.",
            "examples": [
              "public"
            ]
          },
          "display_names": {
            "type": "object",
            "description": "Translated display labels keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Updated Subscription Name"
              }
            ]
          },
          "display_descriptions": {
            "type": "object",
            "description": "Translated descriptions keyed by ISO language code.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "en": "Updated description."
              }
            ]
          }
        }
      },
      "V2SubscriptionsCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful subscription creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/Subscription"
          }
        }
      },
      "V2SubscriptionsUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful subscription update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/Subscription"
          }
        }
      },
      "V2SubscriptionsDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful subscription deletion.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V2SubscriptionsValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body or path parameter validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "CustomField": {
        "type": "object",
        "description": "A contact-scoped custom field definition.",
        "required": [
          "id",
          "name",
          "description",
          "field_type"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "42"
            ]
          },
          "name": {
            "type": "string",
            "readOnly": true,
            "description": "The field identifier. Contains only alphanumeric characters and underscores. Unique per account.",
            "examples": [
              "phone_number"
            ]
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the field. Returns an empty string when not set.",
            "examples": [
              "Customer's mobile phone number",
              ""
            ]
          },
          "field_type": {
            "type": "string",
            "readOnly": true,
            "enum": [
              "text",
              "note",
              "date",
              "url"
            ],
            "description": "The data type of values stored in this field. Read-only after creation.",
            "examples": [
              "text"
            ]
          }
        }
      },
      "CustomFieldCreate": {
        "type": "object",
        "description": "Request body for creating a new custom field.",
        "required": [
          "name",
          "field_type"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_]+$",
            "description": "The field identifier. Alphanumeric characters and underscores only. Must be unique per account.",
            "examples": [
              "phone_number"
            ]
          },
          "field_type": {
            "type": "string",
            "enum": [
              "text",
              "note",
              "date",
              "url"
            ],
            "description": "The data type for values stored in this field. Cannot be changed after creation.",
            "examples": [
              "text"
            ]
          },
          "description": {
            "type": "string",
            "description": "Optional human-readable description of the field.",
            "examples": [
              "Customer's mobile phone number"
            ]
          }
        }
      },
      "CustomFieldUpdate": {
        "type": "object",
        "description": "Request body for updating an existing custom field. At least one property must be provided. `field_type` cannot be changed.",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_]+$",
            "description": "New name for the field. Alphanumeric characters and underscores only. Must be unique per account.",
            "examples": [
              "mobile_number"
            ]
          },
          "description": {
            "type": "string",
            "description": "Updated description for the field.",
            "examples": [
              "Customer's mobile phone number"
            ]
          }
        }
      },
      "V2CustomfieldsCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/CustomField"
          }
        }
      },
      "V2CustomfieldsUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/CustomField"
          }
        }
      },
      "V2CustomfieldsDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful deletion.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0,
            "description": "Always an empty array on deletion."
          }
        }
      },
      "V2CustomfieldsValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "V2CustomfieldsErrorResponse": {
        "type": "object",
        "description": "Returned for general errors (e.g. not found, bad request).",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "description": "Human-readable summary of the error.",
            "examples": [
              "Item with id '42' not found",
              "An 'id' must be provided in order to update"
            ]
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "Group": {
        "type": "object",
        "description": "A group definition.",
        "required": [
          "id",
          "name",
          "parent_group"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "7"
            ]
          },
          "name": {
            "type": "string",
            "description": "The group name.",
            "examples": [
              "Academic Programs"
            ]
          },
          "parent_group": {
            "type": "string",
            "description": "Name of the parent group. Empty string (`\"\"`) when the group is top-level (has no parent).",
            "examples": [
              "Academic Programs",
              ""
            ]
          }
        }
      },
      "GroupCreate": {
        "type": "object",
        "description": "Request body for creating a new group. `name` is required.",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The group name.",
            "examples": [
              "Academic Programs"
            ]
          },
          "parent_group": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Parent group name (string) or parent group ID (integer) to nest this group under. Omit, pass `null`, or pass `\"\"` to create a top-level group. The parent must itself be a top-level group — nesting deeper than one level is not supported.",
            "examples": [
              "Academic Programs",
              7,
              null
            ]
          }
        }
      },
      "GroupUpdate": {
        "type": "object",
        "description": "Request body for updating a group. At least one field must be provided. If `id` is included, it must match the `{id}` in the URL.",
        "minProperties": 1,
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Optional. If provided, must match the `{id}` in the URL. A mismatch returns a validation envelope.",
            "examples": [
              7
            ]
          },
          "name": {
            "type": "string",
            "description": "New name for the group.",
            "examples": [
              "Updated Group Name"
            ]
          },
          "parent_group": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Parent group name or ID to assign. Pass `null` or `\"\"` to promote the group to top-level (remove its parent). The parent must itself be top-level.",
            "examples": [
              "Academic Programs",
              7,
              null,
              ""
            ]
          }
        }
      },
      "V2GroupsCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful group creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/Group"
          }
        }
      },
      "V2GroupsUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful group update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/Group"
          }
        }
      },
      "V2GroupsDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful group deletion.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V2GroupsValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body or path parameter validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "Template": {
        "type": "object",
        "description": "An email template definition.",
        "required": [
          "id",
          "external_id",
          "preview_url",
          "name",
          "status",
          "language",
          "group",
          "create_time",
          "modified_time"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "42"
            ]
          },
          "external_id": {
            "type": "string",
            "readOnly": true,
            "description": "Randomly assigned external identifier used to construct the preview URL.",
            "examples": [
              "a1b2c3d4e5f6g7h8"
            ]
          },
          "preview_url": {
            "type": "string",
            "readOnly": true,
            "description": "Public URL to preview the template. Built on the account's custom domain when it has one, otherwise Envoke's message domain (not the API host), from the account's external ID and the template's external_id.",
            "examples": [
              "https://engage.envoke.com/m/acme-corp/m/a1b2c3d4e5f6g7h8"
            ]
          },
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "The template name. Minimum 3 characters.",
            "examples": [
              "Welcome Email"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "unpublished"
            ],
            "description": "Publication status of the template. New templates are always created as `published`.",
            "examples": [
              "published"
            ]
          },
          "language": {
            "type": "string",
            "description": "IETF language code for the template (e.g. `en`, `fr`). Defaults to `en`.",
            "examples": [
              "en"
            ]
          },
          "group": {
            "type": "string",
            "description": "Name of the group the template belongs to. Empty string (`\"\"`) when no group is assigned.",
            "examples": [
              "Marketing Templates",
              ""
            ]
          },
          "create_time": {
            "type": "string",
            "readOnly": true,
            "description": "Timestamp when the template was created, in the account's configured timezone. Format: `Y-m-d H:i:s`.",
            "examples": [
              "2026-04-11 09:00:00"
            ]
          },
          "modified_time": {
            "type": "string",
            "readOnly": true,
            "description": "Timestamp when the template was last modified, in the account's configured timezone. Format: `Y-m-d H:i:s`.",
            "examples": [
              "2026-04-11 14:22:10"
            ]
          }
        }
      },
      "TemplateWithHTML": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Template"
          },
          {
            "type": "object",
            "required": [
              "html"
            ],
            "properties": {
              "html": {
                "type": "string",
                "description": "Full HTML content of the template. Only present when `?include_html=1` is passed on a single-template GET request.",
                "examples": [
                  "<html><body><h1>Hello!</h1></body></html>"
                ]
              }
            }
          }
        ]
      },
      "TemplateCreate": {
        "type": "object",
        "description": "Request body for creating a new template. Either `html` or `template_id` must be provided. `name` is optional — if omitted, it is auto-generated as `API Template {timestamp}`.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "The template name. Minimum 3 characters. Auto-generated if omitted.",
            "examples": [
              "Welcome Email"
            ]
          },
          "html": {
            "type": "string",
            "description": "Full HTML content of the template. Maximum 200 KB. Required if `template_id` is not provided.",
            "examples": [
              "<html><body><h1>Welcome!</h1></body></html>"
            ]
          },
          "template_id": {
            "type": "integer",
            "description": "ID of an existing template or message to copy HTML from. Required if `html` is not provided. Must be owned by your account or accessible as a shared template.",
            "examples": [
              10
            ]
          },
          "language": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Language for the template. Accepts an IETF language code (e.g. `en`, `fr`) or a numeric language ID. Defaults to `en` if omitted.",
            "examples": [
              "en",
              1
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the template to. Accepts the group name (string) or group ID (integer). The group must be of type `message_template`.",
            "examples": [
              "Marketing Templates",
              5
            ]
          }
        }
      },
      "TemplateUpdate": {
        "type": "object",
        "description": "Request body for updating a template. At least one field must be provided. `external_id`, `preview_url`, `create_time`, and `modified_time` are silently ignored if included. `id` in the request body is validated against the URL id — if they differ, an error is returned.",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "New name for the template. Minimum 3 characters.",
            "examples": [
              "Updated Welcome Email"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "unpublished"
            ],
            "description": "New publication status.",
            "examples": [
              "unpublished"
            ]
          },
          "language": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Language for the template. Accepts an IETF language code or numeric language ID.",
            "examples": [
              "fr"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Group to assign the template to. Accepts group name or group ID. Pass an empty string (`\"\"`) to remove the group assignment.",
            "examples": [
              "Seasonal Campaigns",
              ""
            ]
          },
          "html": {
            "type": "string",
            "description": "Replacement HTML content for the template. Maximum 200 KB.",
            "examples": [
              "<html><body><h1>Updated content</h1></body></html>"
            ]
          }
        }
      },
      "V2TemplatesCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful template creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/Template"
          }
        }
      },
      "V2TemplatesUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful template update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/Template"
          }
        }
      },
      "V2TemplatesDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful deletion. Templates are soft-deleted.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V2TemplatesValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body or path parameter validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              },
              "allowed_values": {
                "type": "object",
                "description": "Present on some errors. Maps a field name to its allowed values.",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "V2TemplatesErrorResponse": {
        "type": "object",
        "description": "Returned for general request errors.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "Message": {
        "type": "object",
        "description": "A message as returned by GET and by the create, update, and cancel responses. Every readable field is present unless `fields` selects some. An unset text value is `\"\"`, except `sender` and `reply_to`, which are `null` when no identity is set. `html` is present only on a single GET with `include_html=1`, as the last key.",
        "required": [
          "id",
          "external_id",
          "preview_url",
          "create_time",
          "modified_time",
          "name",
          "group",
          "status",
          "send_time",
          "sender",
          "reply_to",
          "subject",
          "language",
          "recipient_subscription",
          "recipient_tag",
          "recipient_segment",
          "recipient_language",
          "publish_archive",
          "utm_tag_links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Envoke message ID, assigned on creation. Returned as a string (the one exception is `result_data.id` of `POST /messages/send`). Ignored on write.",
            "examples": [
              "2001"
            ]
          },
          "external_id": {
            "type": "string",
            "readOnly": true,
            "minLength": 32,
            "maxLength": 32,
            "description": "32-character externally visible identifier used to build `preview_url`. Not the same as `id`. Ignored on write.",
            "examples": [
              "3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f"
            ]
          },
          "preview_url": {
            "type": "string",
            "format": "uri",
            "readOnly": true,
            "description": "Public URL that shows the message in a browser without logging in: `https://{domain}/m/{account_external_id}/m/{external_id}`, where `{domain}` is the account's custom domain if it has one and otherwise Envoke's message domain (not the API host). Ignored on write.",
            "examples": [
              "https://engage.envoke.com/m/a7c3e9f1b5d2/m/3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f"
            ]
          },
          "create_time": {
            "type": "string",
            "readOnly": true,
            "description": "When the message was created, `Y-m-d H:i:s` in the account time zone with no offset. Ignored on write. Sortable, not filterable.",
            "examples": [
              "2026-10-06 10:15:42"
            ]
          },
          "modified_time": {
            "type": "string",
            "readOnly": true,
            "description": "When the message was last changed, `Y-m-d H:i:s` in the account time zone with no offset. Changes on every successful update (even one that changes no value), send, and cancel. The list is sorted by this field, newest first, by default. Ignored on write. Sortable, not filterable.",
            "examples": [
              "2026-10-06 11:02:08"
            ]
          },
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "Internal name shown in the Envoke message list; recipients do not see it. At least 3 characters (`The field 'name' must be at least 3 characters long`); a longer name is cut to its first 300 characters without an error. Generated as `API Message {Y-m-d H:i:s}` from Envoke's server clock when omitted on create.",
            "examples": [
              "Spring Season Announcement"
            ]
          },
          "group": {
            "type": "string",
            "description": "Name of the message group (folder), or `\"\"` when the message has no group.",
            "examples": [
              "Productions",
              ""
            ]
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "enum": [
              "Draft",
              "Scheduled",
              "AB test in progress",
              "Queuing",
              "Sending",
              "Transactional",
              "Nurture",
              "Sent"
            ],
            "description": "Current status, returned by name. Codes: `Draft` 0, `Scheduled` 1, `AB test in progress` 2, `Queuing` 4, `Sending` 5, `Transactional` 7, `Nurture` 8, `Sent` 9. Changed only by sending and cancelling; ignored on write. Only a `Draft` can be updated or sent. Filterable by name or code.",
            "examples": [
              "Draft"
            ]
          },
          "send_time": {
            "type": "string",
            "description": "Scheduled send time, `Y-m-d H:i:s` in the account time zone, or `\"\"` when no time is set. Setting it on a draft does not schedule the message. Once set it cannot be cleared, and it survives a cancel.",
            "examples": [
              "2026-10-20 09:00:00",
              ""
            ]
          },
          "sender": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sender identity as `Name <email>`, or `null` when no identity is set. Defaults to the account's default sender on create.",
            "examples": [
              "Box Office <boxoffice@example.org>"
            ]
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reply-to identity as `Name <email>`, or `null` when no identity is set (the default). When not set, replies go to the sender address.",
            "examples": [
              "Front of House <frontofhouse@example.org>",
              null
            ]
          },
          "subject": {
            "type": "string",
            "description": "Subject line, or `\"\"`. Required to send.",
            "examples": [
              "Our spring season is here"
            ]
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "fr"
            ],
            "description": "Language the message is written in. Defaults to `en`.",
            "examples": [
              "en"
            ]
          },
          "recipient_subscription": {
            "type": "string",
            "description": "Name of the subscription the message is sent to, or `\"\"`.",
            "examples": [
              "Season News",
              ""
            ]
          },
          "recipient_tag": {
            "type": "string",
            "description": "Name of the tag the message is sent to, or `\"\"`. `\"\"` when the message is sent to a subscription alone.",
            "examples": [
              "Cast Call",
              ""
            ]
          },
          "recipient_segment": {
            "type": "string",
            "description": "Name of the segment the message is sent to, or `\"\"`.",
            "examples": [
              "Lapsed Subscribers",
              ""
            ]
          },
          "recipient_language": {
            "type": "string",
            "enum": [
              "en",
              "fr",
              ""
            ],
            "description": "Only contacts with this language are sent the message; `\"\"` for all languages. When it matches the account's default language, contacts with no language set are included too.",
            "examples": [
              "",
              "fr"
            ]
          },
          "publish_archive": {
            "type": "boolean",
            "description": "Whether the message is published to the account's public message archive. Defaults to `false`.",
            "examples": [
              true
            ]
          },
          "utm_tag_links": {
            "type": "boolean",
            "description": "Whether UTM tracking parameters are added to the links in the message. Defaults on create to the account's link analytics preference.",
            "examples": [
              true
            ]
          },
          "html": {
            "type": "string",
            "description": "The message HTML. Present only on `GET /messages/{id}?include_html=1`, as the last key.",
            "examples": [
              "<html><body><h1>The Pirates of Penzance</h1><p>Curtain at 7:30 p.m.</p></body></html>"
            ]
          }
        }
      },
      "V2MessagesReplace": {
        "type": "object",
        "description": "Placeholder text to replace in the message HTML once, when the message is saved; the HTML is stored with the replacements made and nothing is replaced at send time. Every key must start with `{` and end with `}`; every value must be a string and is inserted as is, without escaping, so it can contain HTML. Keys are applied in the order sent, so a later key can match text inserted by an earlier value. A key that does not appear in the HTML is ignored, and `{}` is ignored. A key may not contain `{@pref-`, `{@consent-url`, or `{@consent-prefpage-url`. Any other shape (a list, a non-string value, a key without braces, a protected key) fails with `Invalid 'replace' data`. On create it applies to `html` or to the HTML copied through `template_id`; on update it needs `html` in the same request (`The field 'html' is required for content 'replace' data`). Envoke merge tags such as `{@first_name}` are not protected; avoid them as keys. Never returned.",
        "propertyNames": {
          "pattern": "^\\{.+\\}$"
        },
        "additionalProperties": {
          "type": "string"
        },
        "examples": [
          {
            "{SHOW_TITLE}": "The Pirates of Penzance",
            "{CURTAIN_TIME}": "7:30 p.m."
          }
        ]
      },
      "MessageWritable": {
        "type": "object",
        "description": "Writable message fields shared by create, update, send, and cancel bodies. `id` (on create), `external_id`, `preview_url`, `create_time`, `modified_time`, and `status` are read-only and silently ignored. Any other key gives `'{key}' is not a valid field`. Lookup fields (`group`, `sender`, `reply_to`, `language`, `recipient_*`) are cleared with `\"\"` or the string `\"0\"`; a JSON number `0` fails as an invalid value (e.g. `Invalid 'group': '0'`).",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "At least 3 characters: `The field 'name' must be at least 3 characters long`. Cannot be cleared.",
            "examples": [
              "Spring Season Announcement"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Name or ID of an existing message group. Names are matched ignoring case, with a space and an underscore treated as the same. Groups are not created automatically; template and contact groups are not accepted. Unknown: `Invalid 'group': '{value}'` with `allowed_values.group`. `\"\"` or `\"0\"` removes the group.",
            "examples": [
              "Productions",
              31
            ]
          },
          "subject": {
            "type": "string",
            "minLength": 6,
            "description": "At least 6 characters: `The field 'subject' must be at least 6 characters long`; a longer subject is cut to its first 360 characters without an error. Required to send. Cannot be cleared.",
            "examples": [
              "Our spring season is here"
            ]
          },
          "sender": {
            "type": [
              "string",
              "integer"
            ],
            "description": "A sender identity, resolved in this order: an identity ID (`412`); `Name <email>` matching an identity, ignoring case and treating a space and an underscore as the same; `\"\"` or `\"0\"` clears it (on update, with no fallback to the default, and the message cannot then be sent: `The field 'sender' must be set`); an email address, matched exactly including case against the first identity with that address (`No sender identity found for email: '{value}'`); anything else gives `Invalid 'sender': '{value}'`. Errors include `allowed_values.sender` keyed by ID as `Name <email>`. Defaults on create (or when sent as `\"\"`) to the account's default sender.",
            "examples": [
              "boxoffice@example.org",
              412,
              "Box Office <boxoffice@example.org>"
            ]
          },
          "reply_to": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Same formats and resolution as `sender`; errors read `Invalid 'reply_to': '{value}'` and `No reply_to identity found for email: '{value}'`. No default. `\"\"` or `\"0\"` clears it.",
            "examples": [
              413
            ]
          },
          "language": {
            "type": [
              "string",
              "integer"
            ],
            "description": "`en` or `fr` (case-insensitive), or the IDs `1` (English) or `3` (French). `\"\"` or `\"0\"` sets `en`. Otherwise `Invalid 'language': '{value}'` with `allowed_values.language` `{\"1\":\"en\",\"3\":\"fr\"}`.",
            "examples": [
              "fr"
            ]
          },
          "recipient_subscription": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Name or ID of a subscription, including the account's mandatory consent subscription (which sends the message as a mandatory, consent-exempt message, with the subscription's description as the stated reason). With a tag or segment, the subscription narrows it: only contacts in the tag or segment and subscribed are sent the message. Unknown: `Invalid 'recipient_subscription': '{value}'` with `allowed_values` when the account has 50 or fewer. On update, sending any one of the three recipient fields clears the other two unless they are in the same request; `{\"recipient_subscription\": \"\"}` clears all three.",
            "examples": [
              "Season News"
            ]
          },
          "recipient_tag": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Name or ID of a tag. Not with `recipient_segment`: `Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided`. Unknown: `Invalid 'recipient_tag': '{value}'`. Subject to the update rule described under `recipient_subscription`.",
            "examples": [
              "Cast Call"
            ]
          },
          "recipient_segment": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Name or ID of a segment. Segments cannot be created through any API. Not with `recipient_tag`. Unknown: `Invalid 'recipient_segment': '{value}'`. Subject to the update rule described under `recipient_subscription`.",
            "examples": [
              "Lapsed Subscribers"
            ]
          },
          "recipient_language": {
            "type": [
              "string",
              "integer"
            ],
            "description": "`en`, `fr`, `1`, or `3`: send only to contacts with that language. `\"\"` or `\"0\"` clears it (all languages). Never cleared by the recipient update rule. Otherwise `Invalid 'recipient_language': '{value}'`.",
            "examples": [
              "en"
            ]
          },
          "publish_archive": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "`true`, `\"true\"`, `1`, `\"1\"`, `\"yes\"`, or `\"on\"` publishes the message to the web archive; anything else is `false`. Defaults to `false`.",
            "examples": [
              true
            ]
          },
          "utm_tag_links": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "Accepted values as for `publish_archive`. Defaults on create to `true` if link analytics is on in the account preferences, otherwise `false`.",
            "examples": [
              true
            ]
          },
          "send_time": {
            "type": "string",
            "description": "A future date and time, read as account-local wall-clock time, e.g. `2026-10-20 09:00:00`. An offset or `Z` is dropped and the remaining time is still read in the account time zone. Stored only: it does not schedule the message; it is used when the message is sent. A time in the past gives `The field 'send_time' must be in the future`; a value that is not a date gives `Invalid 'send_time': '{value}'`; `\"\"` or `null` is read as the current time and fails as not in the future, so `send_time` cannot be cleared.",
            "examples": [
              "2026-10-20 09:00:00"
            ]
          },
          "html": {
            "type": "string",
            "maxLength": 204800,
            "description": "The complete message HTML, stored as sent without checks. Over 204,800 bytes fails with `The field 'html' may not be longer than 200kB`; the size is checked before `replace` is applied. `\"\"` counts as not sent, so content cannot be blanked. On update it replaces the whole content. HTML containing Envoke's `<meta name=\"TEMPLATE_VERSION\"` tag opens in the drag-and-drop editor in Envoke; other HTML opens in the HTML editor.",
            "examples": [
              "<html><body><h1>Spring Season</h1><p>Four shows, one season pass.</p></body></html>"
            ]
          },
          "replace": {
            "$ref": "#/components/schemas/V2MessagesReplace"
          }
        }
      },
      "MessageCreate": {
        "description": "Create body. One of `html` or `template_id` is required (`One of the fields 'template_id' or 'html' must be provided in order to create a message`); when both are sent, `html` is used and `template_id` is ignored. A message can be created without a subject, sender, or recipients; they are checked when it is sent. Checks run in order and the first six return a single error: empty body, `html` size, `template_id` not found, neither content field, tag and segment together, invalid `replace`; the field checks are then collected and returned together. Defaults: `name` `API Message {Y-m-d H:i:s}`, `status` `Draft`, `group` `\"\"`, `sender` the account default, `reply_to` `null`, `subject` `\"\"`, `language` `en`, recipient fields `\"\"`, `publish_archive` `false`, `utm_tag_links` from the account's link analytics preference, `send_time` `\"\"`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/MessageWritable"
          },
          {
            "type": "object",
            "properties": {
              "template_id": {
                "type": [
                  "integer",
                  "string"
                ],
                "description": "ID of a template, or of another message in the account, whose HTML the new message copies; templates shared with the account can be used. A numeric string is accepted. Only the HTML (and which editor the content opens in) is copied, not the subject, sender, reply-to, language, group, or recipients; the copy is independent of the source. Not found: `Nothing found for 'template_id': {value}`. Create only; never returned.",
                "examples": [
                  1901
                ]
              }
            }
          }
        ],
        "anyOf": [
          {
            "required": [
              "html"
            ]
          },
          {
            "required": [
              "template_id"
            ]
          }
        ]
      },
      "MessageUpdate": {
        "description": "Update body: only the fields to change. Fields left out keep their values, except that the recipient fields are replaced as a set. `template_id` gives `The field 'template_id' is not valid for updating a message`. Checks run in order and each of the first six returns on its own: empty body, message not found, not a `Draft`, `template_id` sent, `html` size, tag and segment together; the field checks are then collected and returned together, before the `replace` checks.",
        "allOf": [
          {
            "$ref": "#/components/schemas/MessageWritable"
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "integer"
                ],
                "description": "Optional. If sent, it must match the ID in the URL (`\"2001\"` and `2001` count as the same); otherwise the request fails with the plain envelope `Provided 'id': {url id} does not match 'id' in request body ({body id})`.",
                "examples": [
                  "2002"
                ]
              }
            }
          }
        ]
      },
      "V2MessagesSendRequest": {
        "description": "Optional body for `POST /messages/{id}/send` and `POST /messages/{id}/cancel`: any update field. On send, the fields are saved first, exactly as a `PATCH` would, and stay saved even if the send then fails; most often the body is `send_time`. On cancel, they are applied after the message is returned to `Draft`; if one fails validation, the message is already a `Draft` but the response is the validation error. An empty body is accepted.",
        "$ref": "#/components/schemas/MessageUpdate"
      },
      "Recipients": {
        "type": "object",
        "description": "The recipient estimate, taken at the time of the request. The recipients are selected again when the send starts, so the actual number can differ. The counts overlap: one contact can be excluded for several reasons and is counted under each.",
        "required": [
          "total_selected",
          "emails_to_be_sent",
          "excluded_unsubscribed_contacts",
          "excluded_invalid_emails",
          "excluded_bounced_emails",
          "excluded_unverified_emails",
          "excluded_by_language"
        ],
        "properties": {
          "total_selected": {
            "type": "integer",
            "description": "Every contact in the tag, subscription, or segment (after a subscription narrows a tag or segment).",
            "examples": [
              412
            ]
          },
          "emails_to_be_sent": {
            "type": "integer",
            "description": "Contacts who pass every check; the estimated number of emails.",
            "examples": [
              377
            ]
          },
          "excluded_unsubscribed_contacts": {
            "type": "integer",
            "description": "Contacts whose consent does not allow sending, such as `Not Provided`, `Revoked`, or `Expired`. On a mandatory consent message, only contacts whose consent blocks all email (such as a spam report).",
            "examples": [
              29
            ]
          },
          "excluded_invalid_emails": {
            "type": "integer",
            "description": "Contacts whose email address is not valid.",
            "examples": [
              2
            ]
          },
          "excluded_bounced_emails": {
            "type": "integer",
            "description": "Contacts whose address has been marked undeliverable.",
            "examples": [
              4
            ]
          },
          "excluded_unverified_emails": {
            "type": "integer",
            "description": "Contacts who have not confirmed their address; only when the account requires email confirmation (double opt-in), otherwise `0`.",
            "examples": [
              0
            ]
          },
          "excluded_by_language": {
            "type": "integer",
            "description": "Contacts whose language does not match `recipient_language`.",
            "examples": [
              0
            ]
          }
        }
      },
      "V2MessagesSendSuccessResponse": {
        "type": "object",
        "description": "The message was scheduled. `result_data` is not the message object. `result_text` is the same for an immediate send.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Message scheduled for send"
          },
          "result_data": {
            "type": "object",
            "required": [
              "id",
              "send_time",
              "recipients"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "The message ID, as in the URL.",
                "examples": [
                  "2001"
                ]
              },
              "send_time": {
                "type": "string",
                "description": "When the message will be sent, `Y-m-d H:i:s` in the account time zone. For an immediate send, the time of the request.",
                "examples": [
                  "2026-10-20 09:00:00"
                ]
              },
              "recipients": {
                "$ref": "#/components/schemas/Recipients"
              }
            }
          }
        }
      },
      "CreateAndSendSuccessResponse": {
        "type": "object",
        "description": "As `SendSuccessResponse`, except that `result_data.id` is an integer.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Message scheduled for send"
          },
          "result_data": {
            "type": "object",
            "required": [
              "id",
              "send_time",
              "recipients"
            ],
            "properties": {
              "id": {
                "type": "integer",
                "description": "The new message's ID, as an integer.",
                "examples": [
                  2005
                ]
              },
              "send_time": {
                "type": "string",
                "description": "`Y-m-d H:i:s` in the account time zone; the time of the request for an immediate send.",
                "examples": [
                  "2026-10-06 14:05:31"
                ]
              },
              "recipients": {
                "$ref": "#/components/schemas/Recipients"
              }
            }
          }
        }
      },
      "SendNoRecipientsResponse": {
        "type": "object",
        "description": "`emails_to_be_sent` was `0`: the message was not scheduled and stays a `Draft`. HTTP 400, with the counts and no `errors` key or `send_time`. On `POST /messages/send`, `result_data.id` is the ID of the draft the request created.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Email can't be sent, no contacts selected"
          },
          "result_data": {
            "type": "object",
            "required": [
              "id",
              "recipients"
            ],
            "properties": {
              "id": {
                "type": [
                  "string",
                  "integer"
                ],
                "examples": [
                  "2004"
                ]
              },
              "recipients": {
                "$ref": "#/components/schemas/Recipients"
              }
            }
          }
        }
      },
      "V2MessagesCreateSuccessResponse": {
        "type": "object",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/Message"
          }
        }
      },
      "V2MessagesUpdateSuccessResponse": {
        "type": "object",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/Message"
          }
        }
      },
      "CancelSuccessResponse": {
        "type": "object",
        "description": "The message is back to `Draft` and keeps its `send_time`.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Message reverted to draft status"
          },
          "result_data": {
            "$ref": "#/components/schemas/Message"
          }
        }
      },
      "V2MessagesDeleteResponse": {
        "type": "object",
        "description": "Delete result. `result_data` is always an empty array; the outcome is in `result_text`.",
        "required": [
          "success",
          "result_data",
          "result_text"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          },
          "result_text": {
            "type": "string",
            "examples": [
              "Deleted",
              "Item with id '2099' not found",
              "Message: 'Spring Season Announcement' is currently sending",
              "This message cannot be deleted because it's part of the automated series: \"Welcome Sequence\"",
              "System messages cannot be deleted",
              "Offer messages cannot be deleted"
            ]
          }
        }
      },
      "V2MessagesValidationErrorResponse": {
        "type": "object",
        "description": "Validation errors. `allowed_values` is present only when a lookup field had an unknown value and the account has 50 or fewer options for it.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "allowed_values": {
                "type": "object",
                "description": "Keyed by field name; each value is an object of the valid options keyed by ID. Sender and reply-to options are `Name <email>`.",
                "additionalProperties": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                },
                "examples": [
                  {
                    "group": {
                      "31": "Productions",
                      "32": "Newsletters"
                    }
                  }
                ]
              }
            }
          }
        }
      },
      "V2MessagesPlainErrorResponse": {
        "type": "object",
        "description": "Request-shape, ID-mismatch, and server errors: the message is in `result_text` and `result_data` is an empty array.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "examples": [
              "Invalid JSON",
              "Invalid request",
              "Provided 'id': 2001 does not match 'id' in request body (2002)",
              "Message content error"
            ]
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V1SendReplace": {
        "type": "object",
        "description": "Placeholder text to replace in the message HTML for one recipient, on a recipient object only (there is no request-level `replace`). The replacement is made when each email is delivered; the stored message keeps its placeholders, so the HTML read through the Message API still shows them, and a later batch sent with `id` can fill the same placeholders with new values. Every key must start with `{` and end with `}`; every value must be a string and is inserted as is, without escaping, so it can contain HTML. A key that does not appear in the HTML is ignored. Keys are applied in the order sent, so a later key can match text inserted by an earlier value. A key may not contain `{@pref-`, `{@consent-url`, or `{@consent-prefpage-url`. `{}` is ignored and the recipient is sent the HTML unchanged. `replace` runs before merge fields: with `enable_merge_fields` on, merge tags inside a value are filled in too, and a merge tag used as a key (such as `{@first_name}`) is replaced by the value instead. Only the HTML is changed, not the subject. Any other shape (a list, a non-string value, a key without braces, a protected key) does not fail the request: that recipient is excluded with `excluded_invalid_replace_data`, and no contact is created for a new address.",
        "propertyNames": {
          "pattern": "^\\{.+\\}$"
        },
        "additionalProperties": {
          "type": "string"
        },
        "examples": [
          {
            "{BOAT_NAME}": "Heron",
            "{SEAT}": "8 (stroke)",
            "{CALL_TIME}": "6:15 a.m."
          }
        ]
      },
      "Recipient": {
        "description": "An item of `recipients`: an email address, or an object with `email` and optional `replace`. Only the first item decides the form: if it is a string, every item is read as an address; if it is an object, every item must be an object. Use the same form for the whole list. An object first item without `email` fails with `Invalid 'recipients': items must have the field 'email'`; a later object without `email` is not sent and is returned as `{\"missing_key_field\": true}`. Other keys on an object, such as `first_name`, are ignored: contact details cannot be set through this API. Each address is matched against the account's contacts; send each address once, spelled exactly as it is stored in Envoke, because a repeated item and an address in a different letter case are treated as separate recipients.",
        "oneOf": [
          {
            "type": "string",
            "description": "An email address.",
            "examples": [
              "stroke.seat@example.org"
            ]
          },
          {
            "type": "object",
            "required": [
              "email"
            ],
            "properties": {
              "email": {
                "type": "string",
                "description": "The recipient's email address.",
                "examples": [
                  "stroke.seat@example.org"
                ]
              },
              "replace": {
                "$ref": "#/components/schemas/V1SendReplace"
              }
            }
          }
        ]
      },
      "RecipientById": {
        "description": "An item of `recipient_contact_ids`: a contact ID (an integer or a numeric string), or an object with `id` and optional `replace`. The first-item rule of `Recipient` applies: an object first item without `id` fails with `Invalid 'recipient_contact_ids': items must have the field 'id'`, and a later object without `id` is returned as `{\"missing_key_field\": true}`. An ID that matches no contact in the account is not sent and is returned as `{\"excluded_invalid_ids\": true}`. No contacts are created.",
        "oneOf": [
          {
            "type": "integer",
            "minimum": 1,
            "examples": [
              7001
            ]
          },
          {
            "type": "string",
            "pattern": "^[0-9]+$",
            "examples": [
              "7001"
            ]
          },
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": [
                  "integer",
                  "string"
                ],
                "description": "The contact ID.",
                "examples": [
                  7001
                ]
              },
              "replace": {
                "$ref": "#/components/schemas/V1SendReplace"
              }
            }
          }
        ]
      },
      "V1SendSendRequest": {
        "type": "object",
        "description": "Create a message and send it now. `subject` is required, with one of `html` or `template_id` and one of `recipients` or `recipient_contact_ids`. Every field is write-only; the response returns the message `id`, counts, and one entry per recipient, not the fields sent. Any other key fails with `'{field}' is not a valid field`: this includes `send_time` (every send is immediate), `utm_tag_links` (UTM tracking parameters are never added), `preheader_text`, a top-level `replace`, and `recipient_subscription`, `recipient_tag`, `recipient_segment`, and `recipient_language` (to send to a tag, subscription, or segment, use the Message API). `id` set to `0`, `\"\"`, or `null` counts as not sent. Defaults: `name` `API Message {Y-m-d H:i:s}` from Envoke's server clock; `group` `API Messages`; `sender` the account's default sender; no `reply_to`; `language` `en` (not the account's default language); `is_mandatory`, `track_recipient_activity`, `enable_merge_fields`, and `publish_archive` `false`.",
        "required": [
          "subject"
        ],
        "allOf": [
          {
            "anyOf": [
              {
                "required": [
                  "html"
                ]
              },
              {
                "required": [
                  "template_id"
                ]
              }
            ]
          },
          {
            "anyOf": [
              {
                "required": [
                  "recipients"
                ]
              },
              {
                "required": [
                  "recipient_contact_ids"
                ]
              }
            ]
          }
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 3,
            "description": "Internal name shown in the Envoke message list and in the Message API; recipients do not see it. At least 3 characters: `The field 'name' must be at least 3 characters long`. A name longer than 300 characters is cut to its first 300 without an error. Default `API Message {Y-m-d H:i:s}`, from Envoke's server clock, which can differ from the account time zone.",
            "examples": [
              "Head of the Valley - Crew Call-up"
            ]
          },
          "subject": {
            "type": "string",
            "minLength": 6,
            "description": "Subject line. Missing or empty: `The field 'subject' must be set`. Shorter than 6 characters: `The field 'subject' must be at least 6 characters long`; `\"\"` returns both errors. A subject longer than 360 characters is cut to its first 360 without an error. Required even with `template_id`, because a template's subject is not copied. Per-recipient `replace` does not change it.",
            "examples": [
              "Crew call-up: Head of the Valley"
            ]
          },
          "group": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Name or ID of an existing message group (folder). Names are matched ignoring case, with a space and an underscore treated as the same. Only message groups are accepted, and a group is not created automatically: an unknown value fails with `Invalid 'group': '{value}'` and `allowed_values.group`. `\"\"` or `\"0\"` uses the default. Default: the `API Messages` group, which Envoke creates the first time it is needed; in Envoke its messages are shown in the `API Messages` folder and left out of the All folder and the status folders. A message with a custom group appears in that group's folder and also in All.",
            "examples": [
              "Crew Notices",
              41
            ]
          },
          "sender": {
            "type": [
              "string",
              "integer"
            ],
            "description": "A sender identity, resolved in this order: an identity ID (`512` or `\"512\"`); `Name <email>` matching an identity, ignoring case and treating a space and an underscore as the same; `\"\"` or `\"0\"` (not set, so the default applies); an email address, matched exactly including case against the first identity with that address, else `No sender identity found for email: '{value}'`; anything else gives `Invalid 'sender': '{value}'`. Both errors include `allowed_values.sender`, keyed by ID, as `Name <email>`. Default: the identity marked as the account default. The identity is used as configured.",
            "examples": [
              "Boathouse <boathouse@example.org>",
              "boathouse@example.org",
              512
            ]
          },
          "reply_to": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Same formats and resolution as `sender`; errors read `No reply_to identity found for email: '{value}'` and `Invalid 'reply_to': '{value}'`. No default: when not set, replies go to the sender address.",
            "examples": [
              513
            ]
          },
          "language": {
            "type": [
              "string",
              "integer"
            ],
            "description": "The language the message is written in: `en` or `fr` (case-insensitive), or the IDs `1` (English) or `3` (French). `\"\"` or `\"0\"` sets `en`. Envoke uses it for the text it adds, such as the unsubscribe footer and merge fields; it does not choose recipients. Any other value fails with `Invalid 'language': '{value}'` and `allowed_values.language` `{\"1\":\"en\",\"3\":\"fr\"}`. Default `en`, not the account's default language.",
            "examples": [
              "fr"
            ]
          },
          "is_mandatory": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "Send as a mandatory consent message. `true`, `\"true\"`, `1`, `\"1\"`, `\"yes\"`, or `\"on\"` is true; anything else is `false`, with no error. When true, the message is sent as a consent-exempt message with the mandatory subscription's description, in the message's language, as the stated reason, and existing contacts are sent only when they hold the account's mandatory consent subscription and have not reported the account's email as spam; a contact without it is counted as `excluded_mandatory_consent`, and one who holds it but has reported spam as `excluded_unsubscribed_contacts`. New addresses are created with the Transactional API consent, given the mandatory subscription, and sent the message. When the account has no mandatory consent subscription, every existing contact is excluded as `excluded_mandatory_consent` and the message is not marked consent-exempt. The `excluded_mandatory_consent` summary count appears only when this is true. Default `false`.",
            "examples": [
              true
            ]
          },
          "track_recipient_activity": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "Accepted values as for `is_mandatory`. When true, links are rewritten to record clicks and an open-tracking image is added; when false, links are left as written and no image is added. Default `false`.",
            "examples": [
              true
            ]
          },
          "enable_merge_fields": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "Accepted values as for `is_mandatory`. When true, all Envoke merge fields (such as `{@first_name}`) and the older `{fname}`-style tags are replaced. When false, only these are replaced: `{@pref-0}`, `{@pref-1}`, `{@consent-url}`, and `{@consent-prefpage-url}` (the link to the recipient's email preferences page), `{fname}` and `{lname}` (the contact's names), and `{eid}` (the contact's `external_id`). Either way, when the HTML contains none of `{@pref-`, `{@consent-url`, or `{@consent-prefpage-url`, Envoke adds its standard unsubscribe footer to every email; tags inserted through `replace` values do not count. Default `false`.",
            "examples": [
              true
            ]
          },
          "publish_archive": {
            "type": [
              "boolean",
              "string",
              "integer"
            ],
            "description": "Accepted values as for `is_mandatory`. When true, the message is published to the account's public message archive. Default `false`.",
            "examples": [
              true
            ]
          },
          "html": {
            "type": "string",
            "maxLength": 204800,
            "description": "The complete message HTML, stored as sent without checks. Over 200 kB (204,800 bytes) fails with `The field 'html' may not be longer than 200kB`; only the `html` sent is checked, not HTML copied through `template_id` or the HTML after `replace`. `\"\"` counts as not sent. When both `html` and `template_id` are sent, `html` is used and `template_id` is ignored, with no error. HTML containing the `<meta name=\"TEMPLATE_VERSION\"` tag that Envoke's drag-and-drop editor writes opens in that editor in Envoke; other HTML opens in the HTML editor.",
            "examples": [
              "<html><body><p>You are racing in {BOAT_NAME}, seat {SEAT}.</p></body></html>"
            ]
          },
          "template_id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "ID of a template, or of another message in the account (including an earlier Send API message), whose HTML the new message copies; templates shared with the account can be used too. A numeric string such as `\"1951\"` is accepted. Only the HTML (and which editor the content opens in) is copied; the subject, sender, reply-to, language, and group are not. The copy is independent of later changes to the source. An ID that matches nothing fails with `Nothing found for 'template_id': {value}`. `0`, `\"0\"`, and `\"\"` count as not sent.",
            "examples": [
              1951,
              "1951"
            ]
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "description": "Up to 1,000 email addresses or recipient objects. Missing or `[]`: `The field 'recipients' must be set`. Not a list: `The field 'recipients' must be an array`. More than 1,000: `The field 'recipients' may not contain more than 1000`; every item counts toward the limit, including invalid ones and repeats, and exactly 1,000 is allowed. An address that matches no contact becomes a new contact and is sent the message; an invalid address is not sent and no contact is created.",
            "items": {
              "$ref": "#/components/schemas/Recipient"
            }
          },
          "recipient_contact_ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "description": "Up to 1,000 contact IDs or recipient objects. Wins over `recipients` when both are sent. `[]`: `The field 'recipient_contact_ids' must not be empty`. Not a list: `The field 'recipient_contact_ids' must be an array`. More than 1,000: `The field 'recipient_contact_ids' may not contain more than 1000`.",
            "items": {
              "$ref": "#/components/schemas/RecipientById"
            }
          }
        },
        "additionalProperties": false
      },
      "SendToExistingRequest": {
        "type": "object",
        "description": "Send an existing Send API message to more recipients. Only `id` and `recipients` may be sent. Checks, in order: the Transactional API preference; the message must exist in the account and not be deleted (`No message found for 'id': {id}`, with the value as sent; a template is not found either); it must have been created by the Send API (`Only messages created by the API are valid when providing an 'id'`); then any other field fails with `The field '{field}' is not valid when providing an 'id'`, or for several `The fields '{f1}', '{f2}' are not valid when providing an 'id'`. This includes `recipient_contact_ids`, so recipients are added by email address. The field errors and the `recipients` shape errors are returned together, without `allowed_values`. Recipients take the same forms, limit, and checks as a new message, including per-recipient `replace`, new contacts for unknown addresses, and the consent rules; a message first sent with `is_mandatory` is sent as a mandatory message again. The message's content, subject, sender, reply-to, language, group, tracking, and merge field settings are reused and cannot be changed. Its status is unchanged: a message that is already `Sent` stays `Sent` while the new batch is delivered.",
        "required": [
          "id",
          "recipients"
        ],
        "properties": {
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "The ID of a message created by the Send API. Echoed in the response with its JSON type unchanged.",
            "examples": [
              3006,
              "3006"
            ]
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "description": "As `recipients` on a new message.",
            "items": {
              "$ref": "#/components/schemas/Recipient"
            }
          }
        },
        "additionalProperties": false
      },
      "SendSummary": {
        "type": "object",
        "description": "One count per outcome, all integers. The counts are exclusive: each recipient is counted once, under the first reason that applies, in the order bounced, consent, unverified, invalid address, invalid `replace`. So `emails_to_be_sent` plus the `excluded_` counts equals `total_selected`, except that items missing their key field are counted in `total_selected` only. Keys appear in the order listed here; some are present only in certain requests.",
        "required": [
          "total_selected",
          "emails_to_be_sent",
          "excluded_unsubscribed_contacts",
          "excluded_bounced_emails",
          "excluded_unverified_emails"
        ],
        "properties": {
          "total_selected": {
            "type": "integer",
            "description": "Always present. Every item in the recipient list.",
            "examples": [
              4
            ]
          },
          "emails_to_be_sent": {
            "type": "integer",
            "description": "Always present. Recipients who are sent the message, existing and new: the number of emails queued.",
            "examples": [
              2
            ]
          },
          "excluded_unsubscribed_contacts": {
            "type": "integer",
            "description": "Always present. Existing contacts whose consent does not allow sending, such as `Not Provided`, `Revoked`, or `Expired`.",
            "examples": [
              1
            ]
          },
          "excluded_invalid_emails": {
            "type": "integer",
            "description": "Present when the request uses `recipients`. Addresses that are not valid, whether new or stored on an existing contact.",
            "examples": [
              1
            ]
          },
          "excluded_invalid_ids": {
            "type": "integer",
            "description": "Present when the request uses `recipient_contact_ids`, in place of `excluded_invalid_emails`. IDs that match no contact in the account.",
            "examples": [
              1
            ]
          },
          "excluded_bounced_emails": {
            "type": "integer",
            "description": "Always present. Existing contacts whose address has been marked undeliverable.",
            "examples": [
              0
            ]
          },
          "excluded_unverified_emails": {
            "type": "integer",
            "description": "Always present. Existing contacts who have not confirmed their address, when the account requires confirmation (double opt-in); otherwise `0`.",
            "examples": [
              0
            ]
          },
          "excluded_invalid_replace_data": {
            "type": "integer",
            "description": "Present only when at least one recipient had an invalid `replace`. Recipients excluded for it.",
            "examples": [
              1
            ]
          },
          "excluded_mandatory_consent": {
            "type": "integer",
            "description": "Present only when `is_mandatory` is true (or when `id` names a message first sent with it). Existing contacts who do not hold the account's mandatory consent subscription.",
            "examples": [
              1
            ]
          }
        },
        "additionalProperties": false
      },
      "RecipientResult": {
        "description": "One entry per recipient, in the order of the request. `id` is the contact ID, an integer, for existing and new contacts. `email` is the address as stored on the contact for an existing contact, and as sent for a new address. Entries are keyed by the stored address, so a recipient listed more than once has a single entry.",
        "oneOf": [
          {
            "title": "Existing contact, sent",
            "type": "object",
            "required": [
              "id",
              "email",
              "emails_to_be_sent"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "email": {
                "type": "string"
              },
              "emails_to_be_sent": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "Existing contact, excluded",
            "type": "object",
            "description": "`id`, `email`, and exactly one exclusion key.",
            "required": [
              "id",
              "email"
            ],
            "minProperties": 3,
            "maxProperties": 3,
            "properties": {
              "id": {
                "type": "integer"
              },
              "email": {
                "type": "string"
              },
              "excluded_bounced_emails": {
                "const": true
              },
              "excluded_unsubscribed_contacts": {
                "const": true
              },
              "excluded_mandatory_consent": {
                "const": true
              },
              "excluded_unverified_emails": {
                "const": true
              },
              "excluded_invalid_emails": {
                "const": true
              },
              "excluded_invalid_replace_data": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "New address, created and sent",
            "type": "object",
            "required": [
              "id",
              "email",
              "new_contacts_created",
              "emails_to_be_sent"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "email": {
                "type": "string"
              },
              "new_contacts_created": {
                "const": true
              },
              "emails_to_be_sent": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "New address, not valid",
            "type": "object",
            "description": "No contact is created.",
            "required": [
              "email",
              "excluded_invalid_emails"
            ],
            "properties": {
              "email": {
                "type": "string"
              },
              "excluded_invalid_emails": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "New address, invalid replace",
            "type": "object",
            "description": "No contact is created.",
            "required": [
              "email",
              "excluded_invalid_replace_data"
            ],
            "properties": {
              "email": {
                "type": "string"
              },
              "excluded_invalid_replace_data": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "ID not found",
            "type": "object",
            "description": "The ID is not repeated; match it by position.",
            "required": [
              "excluded_invalid_ids"
            ],
            "properties": {
              "excluded_invalid_ids": {
                "const": true
              }
            },
            "additionalProperties": false
          },
          {
            "title": "Object without its key field",
            "type": "object",
            "description": "A later recipient object without `email` (or `id`). Counted in `total_selected` only.",
            "required": [
              "missing_key_field"
            ],
            "properties": {
              "missing_key_field": {
                "const": true
              }
            },
            "additionalProperties": false
          }
        ],
        "examples": [
          {
            "id": 7001,
            "email": "stroke.seat@example.org",
            "emails_to_be_sent": true
          },
          {
            "id": 7003,
            "email": "cox@example.org",
            "excluded_unsubscribed_contacts": true
          },
          {
            "id": 7005,
            "email": "novice.rower@example.org",
            "new_contacts_created": true,
            "emails_to_be_sent": true
          },
          {
            "email": "kvrc-not-an-email",
            "excluded_invalid_emails": true
          },
          {
            "excluded_invalid_ids": true
          }
        ]
      },
      "V1SendSendSuccessResponse": {
        "type": "object",
        "description": "At least one recipient was sent the message. HTTP 200. The emails are already queued. No dates are returned.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Message is sending"
          },
          "result_data": {
            "type": "object",
            "required": [
              "id",
              "new_contacts_created",
              "summary",
              "recipients"
            ],
            "properties": {
              "id": {
                "type": [
                  "integer",
                  "string"
                ],
                "description": "The new message's ID, an integer. When the request sends `id`, this echoes the value as sent, with its JSON type unchanged (`3006` returns `3006`, `\"3006\"` returns `\"3006\"`). The Message API returns the same message's `id` as a string.",
                "examples": [
                  3001
                ]
              },
              "new_contacts_created": {
                "type": "integer",
                "minimum": 0,
                "description": "Number of addresses created as new contacts. Always `0` with `recipient_contact_ids`.",
                "examples": [
                  0
                ]
              },
              "summary": {
                "$ref": "#/components/schemas/SendSummary"
              },
              "recipients": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RecipientResult"
                }
              }
            },
            "additionalProperties": false
          }
        }
      },
      "NoValidRecipientsResponse": {
        "type": "object",
        "description": "No recipient could be sent the message. HTTP 400. No message is created. `result_data` has `summary` and `recipients` only: no `id`, no `new_contacts_created`, and no `errors` key. A valid new address without an invalid `replace` is always sent, so a request with one never ends this way.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Message not sent, there are no valid recipients"
          },
          "result_data": {
            "type": "object",
            "required": [
              "summary",
              "recipients"
            ],
            "properties": {
              "summary": {
                "$ref": "#/components/schemas/SendSummary"
              },
              "recipients": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RecipientResult"
                }
              }
            },
            "additionalProperties": false
          }
        }
      },
      "V1SendValidationErrorResponse": {
        "type": "object",
        "description": "Validation errors, HTTP 400. Also used for the empty-body error, the `id` errors, and the credit errors (`Message not sent, the account is out of credits`, `Message not sent, the account has not upgraded`). `allowed_values` is present only when `group`, `sender`, `reply_to`, or `language` had an unknown value and the account has 50 or fewer options for it; it is never returned when `id` is sent.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1
              },
              "allowed_values": {
                "type": "object",
                "description": "Keyed by field name; each value is an object of the valid options keyed by ID. Sender and reply-to options are `Name <email>`.",
                "additionalProperties": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                },
                "examples": [
                  {
                    "group": {
                      "41": "Crew Notices",
                      "42": "API Messages"
                    }
                  }
                ]
              }
            },
            "additionalProperties": false
          }
        }
      },
      "V1SendPlainErrorResponse": {
        "type": "object",
        "description": "Request-shape and content errors: the message is in `result_text` and `result_data` is an empty array. HTTP 400 (401 for invalid credentials).",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "examples": [
              "Invalid JSON",
              "Invalid request",
              "Message content error"
            ]
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "InternalErrorResponse": {
        "type": "object",
        "description": "The message or its recipients could not be saved. HTTP 400, with no `result_data` key at all. Contact Envoke support if it persists.",
        "required": [
          "success",
          "result_text"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Internal error; please contact Envoke support"
          }
        },
        "additionalProperties": false
      },
      "DkimStatus": {
        "type": "object",
        "description": "DKIM verification status for the sender's domain.",
        "required": [
          "verified",
          "domain"
        ],
        "properties": {
          "verified": {
            "type": "boolean",
            "description": "true if DKIM is configured and verified for this domain; false if setup is incomplete."
          },
          "domain": {
            "type": "string",
            "description": "The domain extracted from the sender's email address.",
            "examples": [
              "harborview.gov"
            ]
          }
        }
      },
      "Sender": {
        "type": "object",
        "description": "A sender identity.",
        "required": [
          "id",
          "name",
          "email",
          "is_default"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique identifier assigned on creation. Returned as a string.",
            "examples": [
              "42"
            ]
          },
          "name": {
            "type": "string",
            "description": "The sender display name shown in the From field.",
            "examples": [
              "City of Harborview"
            ]
          },
          "email": {
            "type": "string",
            "description": "The sender email address. Must be a domain the organisation controls.",
            "examples": [
              "communications@harborview.gov"
            ]
          },
          "is_default": {
            "type": "boolean",
            "readOnly": true,
            "description": "true if this is the account's default sender identity, false otherwise. Exactly one identity is always the default. Use POST /senders/{id}/default to change it."
          }
        }
      },
      "SenderFull": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Sender"
          },
          {
            "type": "object",
            "required": [
              "dkim_status"
            ],
            "properties": {
              "dkim_status": {
                "$ref": "#/components/schemas/DkimStatus",
                "description": "DKIM verification status for the sender's domain. Returned on create and update only. When verified is false, configure DKIM DNS records for this domain to improve deliverability."
              }
            }
          }
        ]
      },
      "SenderCreate": {
        "type": "object",
        "description": "Request body for creating a new sender identity. Both name and email are required.",
        "required": [
          "name",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Sender display name. Double quotes are not allowed.",
            "examples": [
              "City of Harborview"
            ]
          },
          "email": {
            "type": "string",
            "description": "Sender email address. Must use a domain the organisation controls. Free and personal email providers are not accepted.",
            "examples": [
              "communications@harborview.gov"
            ]
          }
        }
      },
      "SenderUpdate": {
        "type": "object",
        "description": "Request body for updating a sender identity. At least one of name or email must be provided. If id is included in the body it must match the {id} in the URL.",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "description": "Updated sender display name. Double quotes are not allowed.",
            "examples": [
              "Office of Communications"
            ]
          },
          "email": {
            "type": "string",
            "description": "Updated sender email address.",
            "examples": [
              "info@harborview.gov"
            ]
          }
        }
      },
      "V1SendersCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful sender creation.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Created"
          },
          "result_data": {
            "$ref": "#/components/schemas/SenderFull"
          }
        }
      },
      "V1SendersUpdateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful sender update.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "$ref": "#/components/schemas/SenderFull"
          }
        }
      },
      "V1SendersDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on successful sender deletion.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Deleted"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "SetDefaultSuccessResponse": {
        "type": "object",
        "description": "Returned when the default sender is successfully updated.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string",
            "const": "Updated"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V1SendersErrorResponse": {
        "type": "object",
        "description": "Returned for general request errors (not validation failures).",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V1SendersValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body or path parameter validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "Metrics": {
        "type": "object",
        "description": "The ten metrics, each a count of distinct contacts, in this order. Envoke stores one activity record per message, event type, sub-type and contact, so no count of repeated events exists.",
        "required": [
          "sends",
          "delivered",
          "bounces",
          "hard_bounces",
          "soft_bounces",
          "opens",
          "clicks",
          "unsubscribes",
          "spam_complaints",
          "skips"
        ],
        "properties": {
          "sends": {
            "type": "integer",
            "description": "Distinct contacts with a send record (type 1, any sub-type). Equals the Sent row of the Sent message report.",
            "examples": [
              412
            ]
          },
          "delivered": {
            "type": "integer",
            "description": "Lifetime: `sends` − `bounces`; greater than or equal to the report's Delivered row. Inside an activity window: distinct contacts with a send record in the window and no bounce record for the message at any time, so never negative and never above the window's `sends` (the report's Delivered row for the same window can be negative). Will switch to confirmed delivery records (type 7 sub-type 11) once they are backfilled; the change will be announced.",
            "examples": [
              403
            ]
          },
          "bounces": {
            "type": "integer",
            "description": "Distinct contacts with any bounce record (type 2, sub-types 10, 11, 12, 13, 14, 15, 16), including temporary deferrals. Less than or equal to the report's Bounced row, which counts one per contact per bounce sub-type.",
            "examples": [
              9
            ]
          },
          "hard_bounces": {
            "type": "integer",
            "description": "Distinct contacts with a hard bounce (2/12) or an invalid address (2/16).",
            "examples": [
              5
            ]
          },
          "soft_bounces": {
            "type": "integer",
            "description": "Distinct contacts with an SMTP error (2/10), soft bounce (2/11), temporary deferral (2/14) or rejection (2/15). A contact can be in both hard_bounces and soft_bounces.",
            "examples": [
              4
            ]
          },
          "opens": {
            "type": "integer",
            "description": "Distinct contacts with any open record (type 3, any sub-type). Less than or equal to the report's Opens row, which counts one per contact per email client. Usually no larger than sends, but can be: Envoke records opens for contacts with no send record for the message.",
            "examples": [
              188
            ]
          },
          "clicks": {
            "type": "integer",
            "description": "Distinct contacts with any click record (type 4, any tracked link, including view-online, preferences and poll answers). Equals the report's Clicks row.",
            "examples": [
              57
            ]
          },
          "unsubscribes": {
            "type": "integer",
            "description": "Distinct contacts with a revoked-consent (5/11) or one-click unsubscribe (5/30) record. Between the larger of and the sum of the report's Revoked consent and One-click unsubscribe rows.",
            "examples": [
              4
            ]
          },
          "spam_complaints": {
            "type": "integer",
            "description": "Distinct contacts with a spam report through Envoke (5/12) or from their mailbox provider (5/13).",
            "examples": [
              1
            ]
          },
          "skips": {
            "type": "integer",
            "description": "Distinct contacts selected but not sent the message (type 6, any sub-type). Less than or equal to the report's Skipped row.",
            "examples": [
              6
            ]
          }
        }
      },
      "Rates": {
        "type": "object",
        "description": "The six rates, decimals rounded to four places and returned as JSON numbers (a whole value is 1 or 0 without a decimal point), computed from the Metrics of the same message and window. Normally between 0 and 1; inside an activity window open, click, bounce and unsubscribe can exceed 1 because activity in the window can come from contacts sent the message before it, and a lifetime open or click rate can exceed 1 when opens or clicks were recorded for contacts with no send record. Only on the single-message and compare reports.",
        "required": [
          "delivered",
          "open",
          "click",
          "click_to_open",
          "bounce",
          "unsubscribe"
        ],
        "properties": {
          "delivered": {
            "type": [
              "number",
              "null"
            ],
            "description": "`delivered` ÷ `sends`, rounded to four decimals; `null` when the denominator is 0.",
            "examples": [
              0.9782
            ]
          },
          "open": {
            "type": [
              "number",
              "null"
            ],
            "description": "`opens` ÷ `delivered`, rounded to four decimals; `null` when the denominator is 0.",
            "examples": [
              0.4665
            ]
          },
          "click": {
            "type": [
              "number",
              "null"
            ],
            "description": "`clicks` ÷ `delivered`, rounded to four decimals; `null` when the denominator is 0.",
            "examples": [
              0.1414
            ]
          },
          "click_to_open": {
            "type": [
              "number",
              "null"
            ],
            "description": "`clicks` ÷ `opens`, rounded to four decimals; can exceed 1 because a click needs no recorded open; `null` when `opens` is 0.",
            "examples": [
              0.3032
            ]
          },
          "bounce": {
            "type": [
              "number",
              "null"
            ],
            "description": "`bounces` ÷ `sends`, rounded to four decimals; `null` when `sends` is 0.",
            "examples": [
              0.0218
            ]
          },
          "unsubscribe": {
            "type": [
              "number",
              "null"
            ],
            "description": "`unsubscribes` ÷ `sends`, rounded to four decimals; `null` when `sends` is 0.",
            "examples": [
              0.0097
            ]
          }
        }
      },
      "MessageActivityRow": {
        "type": "object",
        "description": "One row of the message activity list: the message and its ten metrics.",
        "required": [
          "id",
          "name",
          "subject",
          "type",
          "sent_time",
          "recipient_subscription",
          "recipient_tag",
          "recipient_segment",
          "has_variants",
          "sends",
          "delivered",
          "bounces",
          "hard_bounces",
          "soft_bounces",
          "opens",
          "clicks",
          "unsubscribes",
          "spam_complaints",
          "skips"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Envoke message ID; the Message API `id`.",
            "examples": [
              "2001"
            ]
          },
          "name": {
            "type": "string",
            "description": "The message's internal name.",
            "examples": [
              "Spring Season Announcement"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stored subject line with merge tags unreplaced; `null` for a subject-line A/B test, whose versions' subjects are in the single-message report's `variants` (the Message API returns the placeholder `{@ab_test}`); also `null` when the message has no stored subject. Otherwise the stored subject as is, so the literal `{@ab_test}` when that is the stored subject and the A/B block does not vary the subject, or no A/B block remains.",
            "examples": [
              "Our spring season is here, {fname}",
              null
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "campaign",
              "automated"
            ],
            "description": "`campaign` for an email campaign (sent, or sending when `include_in_progress=1`), `automated` for an automated (nurture) message.",
            "examples": [
              "campaign"
            ]
          },
          "sent_time": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the message was launched; for an automated message, when it was set up or last activated. `null` when it has no launch time. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
            "examples": [
              "2026-04-15 10:00:00"
            ]
          },
          "recipient_subscription": {
            "type": "string",
            "description": "The name of the subscription the message was sent to (filter by its ID with subscription_id), or `\"\"`; the Message API value.",
            "examples": [
              "Season News"
            ]
          },
          "recipient_tag": {
            "type": "string",
            "description": "The name of the tag the message was sent to (filter by its ID with tag_id), or `\"\"`; `\"\"` when sent to a subscription alone. The Message API value.",
            "examples": [
              ""
            ]
          },
          "recipient_segment": {
            "type": "string",
            "description": "The name of the segment the message was sent to (filter by its ID with segment_id), or `\"\"`; the Message API value.",
            "examples": [
              ""
            ]
          },
          "has_variants": {
            "type": "boolean",
            "description": "`true` when the message is an A/B test (still `true` when its A/B block no longer exists). Versions are never listed as rows.",
            "examples": [
              false
            ]
          },
          "sends": {
            "type": "integer",
            "description": "Distinct contacts with a send record (type 1, any sub-type). Equals the Sent row of the Sent message report.",
            "examples": [
              412
            ]
          },
          "delivered": {
            "type": "integer",
            "description": "Lifetime: `sends` − `bounces`; greater than or equal to the report's Delivered row. Inside an activity window: distinct contacts with a send record in the window and no bounce record for the message at any time, so never negative and never above the window's `sends` (the report's Delivered row for the same window can be negative). Will switch to confirmed delivery records (type 7 sub-type 11) once they are backfilled; the change will be announced.",
            "examples": [
              403
            ]
          },
          "bounces": {
            "type": "integer",
            "description": "Distinct contacts with any bounce record (type 2, sub-types 10, 11, 12, 13, 14, 15, 16), including temporary deferrals. Less than or equal to the report's Bounced row, which counts one per contact per bounce sub-type.",
            "examples": [
              9
            ]
          },
          "hard_bounces": {
            "type": "integer",
            "description": "Distinct contacts with a hard bounce (2/12) or an invalid address (2/16).",
            "examples": [
              5
            ]
          },
          "soft_bounces": {
            "type": "integer",
            "description": "Distinct contacts with an SMTP error (2/10), soft bounce (2/11), temporary deferral (2/14) or rejection (2/15). A contact can be in both hard_bounces and soft_bounces.",
            "examples": [
              4
            ]
          },
          "opens": {
            "type": "integer",
            "description": "Distinct contacts with any open record (type 3, any sub-type). Less than or equal to the report's Opens row, which counts one per contact per email client. Usually no larger than sends, but can be: Envoke records opens for contacts with no send record for the message.",
            "examples": [
              188
            ]
          },
          "clicks": {
            "type": "integer",
            "description": "Distinct contacts with any click record (type 4, any tracked link, including view-online, preferences and poll answers). Equals the report's Clicks row.",
            "examples": [
              57
            ]
          },
          "unsubscribes": {
            "type": "integer",
            "description": "Distinct contacts with a revoked-consent (5/11) or one-click unsubscribe (5/30) record. Between the larger of and the sum of the report's Revoked consent and One-click unsubscribe rows.",
            "examples": [
              4
            ]
          },
          "spam_complaints": {
            "type": "integer",
            "description": "Distinct contacts with a spam report through Envoke (5/12) or from their mailbox provider (5/13).",
            "examples": [
              1
            ]
          },
          "skips": {
            "type": "integer",
            "description": "Distinct contacts selected but not sent the message (type 6, any sub-type). Less than or equal to the report's Skipped row.",
            "examples": [
              6
            ]
          }
        }
      },
      "BounceBreakdown": {
        "type": "object",
        "description": "Distinct contacts with one bounce reason.",
        "required": [
          "type",
          "label",
          "contacts"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "hard_bounce",
              "soft_bounce",
              "deferred",
              "rejected",
              "invalid_address",
              "other"
            ],
            "description": "Stable key: hard_bounce 2/12, soft_bounce 2/11, deferred 2/14, rejected 2/15, invalid_address 2/16, other 2/10 and the retired 2/13.",
            "examples": [
              "hard_bounce"
            ]
          },
          "label": {
            "type": "string",
            "description": "English display text; may change.",
            "examples": [
              "Hard bounce"
            ]
          },
          "contacts": {
            "type": "integer",
            "description": "Distinct contacts with this reason.",
            "examples": [
              3
            ]
          }
        }
      },
      "PreferenceChange": {
        "type": "object",
        "description": "Distinct contacts with one kind of preference change made from the message.",
        "required": [
          "type",
          "label",
          "contacts"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "revoked_consent",
              "one_click_unsubscribe",
              "spam_reported",
              "subscription_changed",
              "language_changed",
              "express_consent_granted"
            ],
            "description": "Stable key: revoked_consent 5/11, one_click_unsubscribe 5/30, spam_reported 5/12 and 5/13, subscription_changed 5/17, language_changed 5/16, express_consent_granted 5/21.",
            "examples": [
              "revoked_consent"
            ]
          },
          "label": {
            "type": "string",
            "description": "English display text; may change.",
            "examples": [
              "Revoked consent"
            ]
          },
          "contacts": {
            "type": "integer",
            "description": "Distinct contacts with this change.",
            "examples": [
              1
            ]
          }
        }
      },
      "LinkStats": {
        "type": "object",
        "description": "One tracked link in the message. Poll answers are not listed here.",
        "required": [
          "id",
          "url",
          "title",
          "type",
          "clicks"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The tracked link's ID.",
            "examples": [
              "88125"
            ]
          },
          "url": {
            "type": "string",
            "description": "The link as written in the message, with `utm_*` and `eid` parameters removed and merge tags unreplaced, so it can be or contain a merge tag such as `{@customfield-url_test}`; `\"\"` for the view-online and preferences links.",
            "examples": [
              "https://example.org/tickets/pirates"
            ]
          },
          "title": {
            "type": "string",
            "description": "Anchor text, or a shortened URL, cut at 60 bytes (fewer characters for non-ASCII text); a fixed title for view and pref links.",
            "examples": [
              "Get your tickets"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "link",
              "view",
              "pref"
            ],
            "description": "`link` for a content link (including links to pages with Envoke forms), `view` for view online, `pref` for the preferences and unsubscribe links.",
            "examples": [
              "link"
            ]
          },
          "clicks": {
            "type": "integer",
            "description": "Distinct contacts who clicked this link.",
            "examples": [
              39
            ]
          }
        }
      },
      "EmailClients": {
        "type": [
          "object",
          "null"
        ],
        "description": "Open records per email client: every figure counts records (one per contact per email client), not distinct contacts, and this is the only block of the report that does. `null` for messages sent before 1 June 2022.",
        "required": [
          "known_opens",
          "machine_open_records",
          "by_app"
        ],
        "properties": {
          "known_opens": {
            "type": "integer",
            "description": "Open records whose email client was recorded.",
            "examples": [
              214
            ]
          },
          "machine_open_records": {
            "type": "integer",
            "description": "Open records with user agent `Mozilla/5.0` (Apple Mail Privacy Protection), which Envoke classifies as machine opens. A contact has at most one per message, so this also equals the contacts with at least one machine open. There is no machine-opens metric in totals.",
            "examples": [
              47
            ]
          },
          "by_app": {
            "type": "array",
            "description": "Open records per email client, always five entries: Apple Mail (including machine-open records), Gmail, Outlook and Yahoo, most first (ties in that order), then Other, always last and 0 when every client was recognised.",
            "items": {
              "type": "object",
              "required": [
                "name",
                "opens"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Email client.",
                  "examples": [
                    "Apple Mail"
                  ]
                },
                "opens": {
                  "type": "integer",
                  "description": "Open records from this client.",
                  "examples": [
                    88
                  ]
                }
              }
            },
            "examples": [
              [
                {
                  "name": "Apple Mail",
                  "opens": 88
                },
                {
                  "name": "Other",
                  "opens": 14
                }
              ]
            ]
          }
        }
      },
      "UnsubscribeReason": {
        "type": "object",
        "description": "Contacts whose current consent was revoked from this message with this reason. Current state; the whole list is `null` when an activity window is given.",
        "required": [
          "reason",
          "contacts"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "enum": [
              "no_longer_interested",
              "never_subscribed",
              "too_many_emails",
              "irrelevant",
              "other"
            ],
            "description": "Stable reason key.",
            "examples": [
              "too_many_emails"
            ]
          },
          "contacts": {
            "type": "integer",
            "description": "Contacts with this reason now.",
            "examples": [
              1
            ]
          }
        }
      },
      "Poll": {
        "type": "object",
        "description": "One poll in the message. Counts are contacts currently holding each answer; the whole list is `null` when an activity window is given.",
        "required": [
          "id",
          "name",
          "anonymous",
          "answers"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Poll ID.",
            "examples": [
              "61"
            ]
          },
          "name": {
            "type": "string",
            "description": "The poll question.",
            "examples": [
              "Which performance will you attend?"
            ]
          },
          "anonymous": {
            "type": "boolean",
            "description": "`true` for an anonymous poll; counts are still given.",
            "examples": [
              false
            ]
          },
          "answers": {
            "type": "array",
            "description": "Answers in the order the Sent message report lists them: by sort position, then alphabetically by text (poll answers are stored without a sort position, so normally alphabetical, not the order in the message).",
            "items": {
              "type": "object",
              "required": [
                "id",
                "text",
                "contacts"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Answer ID.",
                  "examples": [
                    "7301"
                  ]
                },
                "text": {
                  "type": "string",
                  "description": "Answer text.",
                  "examples": [
                    "Friday 7:30 p.m."
                  ]
                },
                "contacts": {
                  "type": "integer",
                  "description": "Contacts holding this answer now.",
                  "examples": [
                    48
                  ]
                }
              }
            },
            "examples": [
              [
                {
                  "id": "7301",
                  "text": "Friday 7:30 p.m.",
                  "contacts": 48
                }
              ]
            ]
          }
        }
      },
      "Variant": {
        "type": "object",
        "description": "One version of an A/B test, counting the contacts sent that version as the report's version tabs do: the test-phase contacts and, for the winner, the contacts sent the winner after the test. The versions' sends usually add up to the message's sends; less when contacts were sent outside the versions' assignment (overrides, resends), more on old tests that recorded a contact under two versions.",
        "required": [
          "id",
          "name",
          "test_type",
          "subject",
          "sender",
          "winner",
          "totals",
          "rates"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Version ID.",
            "examples": [
              "5122"
            ]
          },
          "name": {
            "type": "string",
            "description": "Version name by position: A, B, C and so on, as in the Sent message report tabs.",
            "examples": [
              "B"
            ]
          },
          "test_type": {
            "type": "string",
            "enum": [
              "subject",
              "preheader",
              "sender",
              "content"
            ],
            "description": "What the test varies.",
            "examples": [
              "subject"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "This version's subject for a subject test, otherwise `null`.",
            "examples": [
              "Curtain up on Pirates, {fname}!"
            ]
          },
          "sender": {
            "type": [
              "string",
              "null"
            ],
            "description": "This version's sender as `Name <email>` for a sender test, otherwise `null`.",
            "examples": [
              null
            ]
          },
          "winner": {
            "type": "boolean",
            "description": "`true` for the winning version; `false` for every version while the test runs or when no winner was recorded.",
            "examples": [
              true
            ]
          },
          "totals": {
            "$ref": "#/components/schemas/Metrics"
          },
          "rates": {
            "$ref": "#/components/schemas/Rates"
          }
        }
      },
      "MessageStats": {
        "type": "object",
        "description": "The full report for one message. `totals` equal the message activity list row for the same message and window.",
        "required": [
          "id",
          "name",
          "subject",
          "type",
          "sent_time",
          "has_variants",
          "totals",
          "rates",
          "bounces",
          "preference_changes",
          "links",
          "email_clients",
          "unsubscribe_reasons",
          "polls",
          "variants"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Envoke message ID; the Message API `id`.",
            "examples": [
              "2001"
            ]
          },
          "name": {
            "type": "string",
            "description": "The message's internal name.",
            "examples": [
              "Spring Season Announcement"
            ]
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stored subject line with merge tags unreplaced; `null` for a subject-line A/B test, whose versions' subjects are in the single-message report's `variants` (the Message API returns the placeholder `{@ab_test}`); also `null` when the message has no stored subject. Otherwise the stored subject as is, so the literal `{@ab_test}` when that is the stored subject and the A/B block does not vary the subject, or no A/B block remains.",
            "examples": [
              "Our spring season is here, {fname}",
              null
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "campaign",
              "automated"
            ],
            "description": "`campaign` for an email campaign (sent, or sending when `include_in_progress=1`), `automated` for an automated (nurture) message.",
            "examples": [
              "campaign"
            ]
          },
          "sent_time": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the message was launched; for an automated message, when it was set up or last activated. `null` when it has no launch time. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
            "examples": [
              "2026-04-15 10:00:00"
            ]
          },
          "has_variants": {
            "type": "boolean",
            "description": "`true` when the message is an A/B test (still `true`, with `variants` `[]`, when its A/B block no longer exists).",
            "examples": [
              true
            ]
          },
          "totals": {
            "$ref": "#/components/schemas/Metrics"
          },
          "rates": {
            "$ref": "#/components/schemas/Rates"
          },
          "bounces": {
            "type": "array",
            "description": "All six bounce reasons in fixed order; entries can add up to more than totals.bounces.",
            "items": {
              "$ref": "#/components/schemas/BounceBreakdown"
            },
            "examples": [
              [
                {
                  "type": "hard_bounce",
                  "label": "Hard bounce",
                  "contacts": 3
                }
              ]
            ]
          },
          "preference_changes": {
            "type": "array",
            "description": "All six preference changes in fixed order.",
            "items": {
              "$ref": "#/components/schemas/PreferenceChange"
            },
            "examples": [
              [
                {
                  "type": "revoked_consent",
                  "label": "Revoked consent",
                  "contacts": 1
                }
              ]
            ]
          },
          "links": {
            "type": "array",
            "description": "One entry per tracked link, most clicked first; entries can add up to more than totals.clicks.",
            "items": {
              "$ref": "#/components/schemas/LinkStats"
            },
            "examples": [
              [
                {
                  "id": "88125",
                  "url": "https://example.org/tickets/pirates",
                  "title": "Get your tickets",
                  "type": "link",
                  "clicks": 39
                }
              ]
            ]
          },
          "email_clients": {
            "$ref": "#/components/schemas/EmailClients"
          },
          "unsubscribe_reasons": {
            "type": [
              "array",
              "null"
            ],
            "description": "Reasons at least one contact chose, in the preferences page's order. Current state, so `null` when `activity_from` or `activity_to` is given.",
            "items": {
              "$ref": "#/components/schemas/UnsubscribeReason"
            },
            "examples": [
              [
                {
                  "reason": "too_many_emails",
                  "contacts": 1
                }
              ]
            ]
          },
          "polls": {
            "type": [
              "array",
              "null"
            ],
            "description": "One entry per poll; `[]` when the message has none. Current state, so `null` when `activity_from` or `activity_to` is given.",
            "items": {
              "$ref": "#/components/schemas/Poll"
            },
            "examples": [
              []
            ]
          },
          "variants": {
            "type": "array",
            "description": "One entry per A/B version, always present when `has_variants` is true (there is no parameter to request or suppress it); `[]` when the message is not an A/B test, and when its A/B block no longer exists.",
            "items": {
              "$ref": "#/components/schemas/Variant"
            },
            "examples": [
              []
            ]
          }
        }
      },
      "CompareMessage": {
        "type": "object",
        "description": "One message in a comparison.",
        "required": [
          "id",
          "name",
          "sent_time",
          "totals",
          "rates"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Envoke message ID; the Message API `id`.",
            "examples": [
              "2001"
            ]
          },
          "name": {
            "type": "string",
            "description": "The message's internal name.",
            "examples": [
              "Spring Season Announcement"
            ]
          },
          "sent_time": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the message was launched; for an automated message, when it was set up or last activated. `null` when it has no launch time. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
            "examples": [
              "2026-04-15 10:00:00"
            ]
          },
          "totals": {
            "$ref": "#/components/schemas/Metrics"
          },
          "rates": {
            "$ref": "#/components/schemas/Rates"
          }
        }
      },
      "CompareResponse": {
        "type": "object",
        "description": "Per-message totals and rates plus a combined block for the set.",
        "required": [
          "messages",
          "combined"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "description": "The messages, newest sent_time first, then highest id.",
            "items": {
              "$ref": "#/components/schemas/CompareMessage"
            },
            "examples": [
              []
            ]
          },
          "combined": {
            "type": "object",
            "description": "The set as a whole. Totals count distinct contacts across the set (a contact sent two messages counts once), so they measure reach and the rates are not averages of the messages' rates. delivered is combined sends − combined bounces for lifetime counts and, inside an activity window, the contacts who for at least one message in the set were sent it in the window and have no bounce for it. Rates are computed from the combined totals.",
            "required": [
              "message_count",
              "totals",
              "rates"
            ],
            "properties": {
              "message_count": {
                "type": "integer",
                "description": "Number of messages in the set (with an activity window, after leaving out messages launched after activity_to).",
                "examples": [
                  2
                ]
              },
              "totals": {
                "$ref": "#/components/schemas/Metrics"
              },
              "rates": {
                "$ref": "#/components/schemas/Rates"
              }
            },
            "examples": [
              {
                "message_count": 2,
                "totals": {
                  "sends": 702,
                  "delivered": 686,
                  "bounces": 16,
                  "hard_bounces": 9,
                  "soft_bounces": 7,
                  "opens": 331,
                  "clicks": 108,
                  "unsubscribes": 6,
                  "spam_complaints": 1,
                  "skips": 9
                },
                "rates": {
                  "delivered": 0.9772,
                  "open": 0.4825,
                  "click": 0.1574,
                  "click_to_open": 0.3263,
                  "bounce": 0.0228,
                  "unsubscribe": 0.0085
                }
              }
            ]
          }
        }
      },
      "ContactActivity": {
        "type": "object",
        "description": "One contact's email activity: who, a summary across every message (inside the window when given), and a page of messages.",
        "required": [
          "contact",
          "summary",
          "messages"
        ],
        "properties": {
          "contact": {
            "type": "object",
            "description": "The contact.",
            "required": [
              "id",
              "email",
              "remote_id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Envoke contact ID; the Contact API `id`.",
                "examples": [
                  "1004"
                ]
              },
              "email": {
                "type": "string",
                "description": "Email address.",
                "examples": [
                  "maya.chen@example.org"
                ]
              },
              "remote_id": {
                "type": "string",
                "description": "Remote ID, or `\"\"`.",
                "examples": [
                  "MRCT-0412"
                ]
              }
            },
            "examples": [
              {
                "id": "1004",
                "email": "maya.chen@example.org",
                "remote_id": "MRCT-0412"
              }
            ]
          },
          "summary": {
            "type": "object",
            "description": "Counts of messages across every message in the report, not only the current page.",
            "required": [
              "sends",
              "delivered",
              "bounces",
              "opens",
              "clicks",
              "unsubscribes",
              "last_send_time",
              "last_open_time",
              "last_click_time"
            ],
            "properties": {
              "sends": {
                "type": "integer",
                "description": "Messages the contact was sent.",
                "examples": [
                  3
                ]
              },
              "delivered": {
                "type": "integer",
                "description": "Messages with delivered true.",
                "examples": [
                  3
                ]
              },
              "bounces": {
                "type": "integer",
                "description": "Messages with bounced true.",
                "examples": [
                  0
                ]
              },
              "opens": {
                "type": "integer",
                "description": "Messages with opened true.",
                "examples": [
                  2
                ]
              },
              "clicks": {
                "type": "integer",
                "description": "Messages with clicked true.",
                "examples": [
                  1
                ]
              },
              "unsubscribes": {
                "type": "integer",
                "description": "Messages with unsubscribed true.",
                "examples": [
                  0
                ]
              },
              "last_send_time": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The most recent send to the contact, or `null`. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                "examples": [
                  "2026-05-14 09:31:07"
                ]
              },
              "last_open_time": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The most recent open record (a first open of a message in an email client), or `null`. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                "examples": [
                  "2026-05-14 12:02:44"
                ]
              },
              "last_click_time": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The most recent click record (a first click on a link), or `null`. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                "examples": [
                  "2026-05-14 12:03:10"
                ]
              }
            },
            "examples": [
              {
                "sends": 3,
                "delivered": 3,
                "bounces": 0,
                "opens": 2,
                "clicks": 1,
                "unsubscribes": 0,
                "last_send_time": "2026-05-14 09:31:07",
                "last_open_time": "2026-05-14 12:02:44",
                "last_click_time": "2026-05-14 12:03:10"
              }
            ]
          },
          "messages": {
            "type": "array",
            "description": "Messages the contact was sent, newest sent_time first, then highest id; paged with limit and skip.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "sent_time",
                "delivered",
                "bounced",
                "opened",
                "first_open_time",
                "opens",
                "clicked",
                "clicks",
                "links",
                "unsubscribed"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Envoke message ID; the Message API `id`.",
                  "examples": [
                    "2001"
                  ]
                },
                "name": {
                  "type": "string",
                  "description": "The message's internal name.",
                  "examples": [
                    "Spring Season Announcement"
                  ]
                },
                "sent_time": {
                  "type": "string",
                  "description": "When this contact was sent the message (for an automated message, this contact's send). `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                  "examples": [
                    "2026-05-14 09:31:07"
                  ]
                },
                "delivered": {
                  "type": "boolean",
                  "description": "Sent and no bounce record for the message. With an activity window, the send must be inside the window; a bounce counts whenever it arrived.",
                  "examples": [
                    true
                  ]
                },
                "bounced": {
                  "type": "boolean",
                  "description": "Any bounce record, including a temporary deferral.",
                  "examples": [
                    false
                  ]
                },
                "opened": {
                  "type": "boolean",
                  "description": "Any open record.",
                  "examples": [
                    true
                  ]
                },
                "first_open_time": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The contact's first open of the message, or `null`. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                  "examples": [
                    "2026-05-14 12:02:44"
                  ]
                },
                "opens": {
                  "type": "integer",
                  "description": "Number of email clients the contact opened the message in.",
                  "examples": [
                    1
                  ]
                },
                "clicked": {
                  "type": "boolean",
                  "description": "Any click record.",
                  "examples": [
                    true
                  ]
                },
                "clicks": {
                  "type": "integer",
                  "description": "Number of different links clicked; the length of `links`.",
                  "examples": [
                    1
                  ]
                },
                "links": {
                  "type": "array",
                  "description": "Links clicked, most recent first.",
                  "items": {
                    "type": "object",
                    "required": [
                      "id",
                      "url",
                      "first_click_time"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Tracked link ID.",
                        "examples": [
                          "88125"
                        ]
                      },
                      "url": {
                        "type": "string",
                        "description": "The link as written in the message, tracking parameters removed and merge tags unreplaced (it can be or contain a merge tag such as `{@customfield-url_test}`); `\"\"` for Envoke's own links (view online, preferences, poll answers).",
                        "examples": [
                          "https://example.org/tickets/pirates"
                        ]
                      },
                      "first_click_time": {
                        "type": "string",
                        "description": "The contact's first click on this link. `YYYY-MM-DD HH:MM:SS` in the account's time zone (as the Message API), no offset.",
                        "examples": [
                          "2026-05-14 12:03:10"
                        ]
                      }
                    }
                  },
                  "examples": [
                    [
                      {
                        "id": "88125",
                        "url": "https://example.org/tickets/pirates",
                        "first_click_time": "2026-05-14 12:03:10"
                      }
                    ]
                  ]
                },
                "unsubscribed": {
                  "type": "boolean",
                  "description": "Revoked consent (5/11) or one-click unsubscribe (5/30) from this message.",
                  "examples": [
                    false
                  ]
                }
              }
            },
            "examples": [
              []
            ]
          }
        }
      },
      "V2ReportsValidationErrorResponse": {
        "type": "object",
        "description": "HTTP 400 validation envelope. Every problem in the request is listed.",
        "required": [
          "success",
          "result_data",
          "result_text"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false,
            "description": "Always false.",
            "examples": [
              false
            ]
          },
          "result_data": {
            "type": "object",
            "description": "The errors.",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One string per problem.",
                "examples": [
                  [
                    "Parameter 'page' is not valid"
                  ]
                ]
              }
            },
            "examples": [
              {
                "errors": [
                  "Parameter 'page' is not valid"
                ]
              }
            ]
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'",
            "description": "Fixed text.",
            "examples": [
              "Validation failed, see 'result_data' -> 'errors'"
            ]
          }
        }
      },
      "V2ReportsPlainErrorResponse": {
        "type": "object",
        "description": "Plain error envelope used by 401, 404, the closed or inactive account 403, the contact activity 403, and the 400 `Invalid request` of a query timeout or database error.",
        "required": [
          "success",
          "result_data",
          "result_text"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Always false.",
            "examples": [
              false
            ]
          },
          "result_data": {
            "type": "array",
            "maxItems": 0,
            "description": "Always empty.",
            "examples": [
              []
            ]
          },
          "result_text": {
            "type": "string",
            "description": "The error.",
            "examples": [
              "Invalid credentials"
            ]
          }
        }
      },
      "EmptyResult": {
        "type": "array",
        "maxItems": 0,
        "description": "`[]`: the message or contact does not exist, belongs to another account, or is deleted (or the message has not started sending); on the message contacts report, also an event no contact has.",
        "examples": [
          []
        ]
      },
      "MessageContactRow": {
        "type": "object",
        "description": "One contact with the requested event for the message. With fields, only the fields named.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Envoke contact ID; the Contact API id.",
            "examples": [
              "1004"
            ]
          },
          "email": {
            "type": "string",
            "description": "The contact's email address now (not necessarily the address the message was sent to).",
            "examples": [
              "maya.chen@example.org"
            ]
          },
          "remote_id": {
            "type": "string",
            "description": "The contact's remote ID now, or \"\".",
            "examples": [
              "MRCT-0412"
            ]
          },
          "event_time": {
            "type": [
              "string",
              "null"
            ],
            "description": "The contact's first record of the event for the message (inside the activity window when one is given), YYYY-MM-DD HH:MM:SS in the account's time zone. sent and delivered: the send; bounced: the first bounce of the reasons asked for; opened: the first open; clicked: the first click (on link_id when given); unsubscribed and spam_reported: the earlier of the two sub-types; skipped: the first skip. null for not_opened and not_clicked.",
            "examples": [
              "2026-05-14 12:02:44"
            ]
          },
          "count": {
            "type": "integer",
            "description": "opened: the number of email clients the contact opened the message in (an Apple Mail Privacy Protection open is one). clicked without link_id: the number of different links clicked. Every other event, and clicked with link_id: 1.",
            "examples": [
              1
            ]
          }
        }
      },
      "HookType": {
        "type": "string",
        "description": "The event type that triggers the webhook delivery. `email_delivery` is reserved for an upcoming release: subscriptions are accepted, but no events are delivered yet.",
        "enum": [
          "new_contact",
          "consent_change",
          "submission",
          "form_submission",
          "preference_page_submission",
          "api_submission",
          "import_submission",
          "ui_submission",
          "email_send",
          "email_open",
          "email_click",
          "email_autoresponder_send",
          "email_bounce",
          "email_spam_report",
          "email_delivery",
          "new_lead",
          "passed_to_sales",
          "lead_update",
          "new_opportunity",
          "new_sale"
        ]
      },
      "HookFilter": {
        "type": "object",
        "description": "Optional key-value conditions evaluated against the event payload before delivery. All conditions must match. An empty object (`{}`) means no filter — every event of the subscribed type is delivered. Keys must be strings; values must be scalar (string, number, or boolean). Common filter keys: `form_id` for form events; `message_id` (integer message ID) or `message_external_id` for email events; `bounce_type` for `email_bounce`; `source` for `email_spam_report`. Keys are matched against the delivered payload, so a key the payload does not contain (for example `campaign_id`) never matches.",
        "additionalProperties": {
          "type": [
            "string",
            "number",
            "boolean"
          ]
        },
        "examples": [
          {},
          {
            "form_id": "abc123def456"
          },
          {
            "message_id": "789"
          },
          {
            "message_external_id": "f5a3c1e9b7d5f3a1c9e7b5d3f1a9c7e5"
          }
        ]
      },
      "Hook": {
        "type": "object",
        "description": "A webhook subscription.",
        "required": [
          "id",
          "type",
          "url",
          "filter"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true,
            "description": "Unique 32-character hex identifier assigned on creation.",
            "examples": [
              "9f1c2a42baedb56c7e65b3eeaf1a8814"
            ]
          },
          "type": {
            "$ref": "#/components/schemas/HookType"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The destination URL that receives the HTTP POST payload.",
            "examples": [
              "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
            ]
          },
          "filter": {
            "$ref": "#/components/schemas/HookFilter"
          }
        }
      },
      "HookCreate": {
        "type": "object",
        "description": "Request body for creating a webhook subscription. `type` and `url` are required.",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/HookType"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The destination URL. Must start with http:// or https://.",
            "examples": [
              "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
            ]
          },
          "filter": {
            "$ref": "#/components/schemas/HookFilter"
          }
        }
      },
      "V1HooksCreateSuccessResponse": {
        "type": "object",
        "description": "Returned on successful webhook subscription creation (HTTP 201). Contains only the new subscription ID.",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The newly created subscription ID.",
            "examples": [
              "9f1c2a42baedb56c7e65b3eeaf1a8814"
            ]
          }
        }
      },
      "V1HooksDeleteSuccessResponse": {
        "type": "object",
        "description": "Returned on deletion. Idempotent — returns success even if the ID did not exist.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "result_text": {
            "type": "string"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      },
      "V1HooksValidationErrorResponse": {
        "type": "object",
        "description": "Returned when request body validation fails.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "Validation failed, see 'result_data' -> 'errors'"
          },
          "result_data": {
            "type": "object",
            "required": [
              "errors"
            ],
            "properties": {
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "One or more human-readable validation error messages."
              }
            }
          }
        }
      },
      "DuplicateErrorResponse": {
        "type": "object",
        "description": "Returned when a subscription with the same type and URL already exists.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string",
            "const": "A webhook subscription with this type and URL already exists"
          },
          "result_data": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "The ID of the existing subscription."
              }
            }
          }
        }
      },
      "V1HooksErrorResponse": {
        "type": "object",
        "description": "Returned for general request errors.",
        "required": [
          "success",
          "result_text",
          "result_data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "result_text": {
            "type": "string"
          },
          "result_data": {
            "type": "array",
            "maxItems": 0
          }
        }
      }
    },
    "responses": {
      "V1ContactsUnauthorized": {
        "description": "Invalid API ID or key. A `WWW-Authenticate: Basic` header is sent, and the body is the plain error envelope. The response's `Content-Type` is `text/html`, although the body is JSON.",
        "content": {
          "text/html": {
            "schema": {
              "$ref": "#/components/schemas/V1ContactsPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "Invalid credentials"
            }
          }
        }
      },
      "V1ContactsForbidden": {
        "description": "Valid key on a closed or inactive account.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_data": {
                  "type": "array",
                  "maxItems": 0
                },
                "result_text": {
                  "type": "string",
                  "enum": [
                    "This account has been closed.",
                    "This account is not active."
                  ]
                }
              },
              "required": [
                "success",
                "result_data",
                "result_text"
              ]
            }
          }
        }
      },
      "V1ContactsTooManyRequests": {
        "description": "The account's API limit was exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_text": {
                  "type": "string",
                  "description": "Names the limit that was exceeded."
                },
                "result_data": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "V2TagsNotFoundGet": {
        "description": "The requested tag ID was not found. Returns an empty array.",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "maxItems": 0
            },
            "example": []
          }
        }
      },
      "V2TagsNotFoundMutate": {
        "description": "The requested tag ID was not found. Returns a validation error envelope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2TagsValidationErrorResponse"
            },
            "example": {
              "success": false,
              "result_text": "Validation failed, see 'result_data' -> 'errors'",
              "result_data": {
                "errors": [
                  "Item with id '999999' not found"
                ]
              }
            }
          }
        }
      },
      "V2SubscriptionsNotFoundGet": {
        "description": "The requested subscription ID was not found. Returns an empty array.",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "maxItems": 0
            },
            "example": []
          }
        }
      },
      "ValidationError": {
        "description": "One or more request body fields failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2CustomfieldsValidationErrorResponse"
            },
            "example": {
              "success": false,
              "result_text": "Validation failed, see 'result_data' -> 'errors'",
              "result_data": {
                "errors": [
                  "The field 'name' is required",
                  "field_type is required"
                ]
              }
            }
          }
        }
      },
      "V2CustomfieldsNotFoundGet": {
        "description": "The requested custom field ID was not found. GET by ID returns an empty array rather than an error envelope.",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "maxItems": 0
            },
            "example": []
          }
        }
      },
      "V2CustomfieldsNotFoundMutate": {
        "description": "The requested custom field ID was not found. PATCH and DELETE return a validation error envelope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2CustomfieldsValidationErrorResponse"
            },
            "example": {
              "success": false,
              "result_text": "Validation failed, see 'result_data' -> 'errors'",
              "result_data": {
                "errors": [
                  "Item with id '42' not found"
                ]
              }
            }
          }
        }
      },
      "V2TemplatesNotFoundGet": {
        "description": "The requested template ID was not found. Returns an empty array.",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "maxItems": 0
            },
            "example": []
          }
        }
      },
      "V2TemplatesNotFoundMutate": {
        "description": "The requested template ID was not found. Returns a validation error envelope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2TemplatesValidationErrorResponse"
            },
            "example": {
              "success": false,
              "result_text": "Validation failed, see 'result_data' -> 'errors'",
              "result_data": {
                "errors": [
                  "Template not found"
                ]
              }
            }
          }
        }
      },
      "V2MessagesUnauthorized": {
        "description": "Invalid API ID or key. A `WWW-Authenticate: Basic` header is sent, and the body is the plain error envelope. The response's `Content-Type` is `text/html`, although the body is JSON.",
        "content": {
          "text/html": {
            "schema": {
              "$ref": "#/components/schemas/V2MessagesPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "Invalid credentials"
            }
          }
        }
      },
      "V2MessagesForbidden": {
        "description": "Valid key on a closed or inactive account.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_data": {
                  "type": "array",
                  "maxItems": 0
                },
                "result_text": {
                  "type": "string",
                  "enum": [
                    "This account has been closed.",
                    "This account is not active."
                  ]
                }
              },
              "required": [
                "success",
                "result_data",
                "result_text"
              ]
            }
          }
        }
      },
      "V2MessagesTooManyRequests": {
        "description": "The account's API limit was exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_text": {
                  "type": "string",
                  "description": "Names the limit that was exceeded."
                },
                "result_data": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "V1SendUnauthorized": {
        "description": "Invalid API ID or key. A `WWW-Authenticate: Basic` header is sent, and the body is the plain error envelope. The response's `Content-Type` is `text/html`, although the body is JSON.",
        "content": {
          "text/html": {
            "schema": {
              "$ref": "#/components/schemas/V1SendPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "Invalid credentials"
            }
          }
        }
      },
      "V1SendForbidden": {
        "description": "Valid key on a closed or inactive account.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_data": {
                  "type": "array",
                  "maxItems": 0
                },
                "result_text": {
                  "type": "string",
                  "enum": [
                    "This account has been closed.",
                    "This account is not active."
                  ]
                }
              },
              "required": [
                "success",
                "result_data",
                "result_text"
              ]
            }
          }
        }
      },
      "V1SendTooManyRequests": {
        "description": "The account's API limit was exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "const": false
                },
                "result_text": {
                  "type": "string",
                  "description": "Names the limit that was exceeded."
                },
                "result_data": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "V1SendersNotFoundMutate": {
        "description": "The requested sender ID was not found. Returns a validation error envelope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V1SendersValidationErrorResponse"
            },
            "example": {
              "success": false,
              "result_text": "Validation failed, see 'result_data' -> 'errors'",
              "errors": [
                "Item with id '42' not found"
              ]
            }
          }
        }
      },
      "V2ReportsUnauthorized": {
        "description": "Invalid API ID or key. JSON body (sent as text/html).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2ReportsPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "Invalid credentials"
            }
          }
        }
      },
      "V2ReportsForbidden": {
        "description": "A valid key on a closed or inactive account: `result_text` is `This account has been closed.` or `This account is not active.`",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2ReportsPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "This account has been closed."
            }
          }
        }
      },
      "V2ReportsTooManyRequests": {
        "description": "The account's API limit was exceeded; `success: false` and a `result_text` naming the limit.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object"
            }
          }
        }
      },
      "NotFound": {
        "description": "A non-numeric id in the path, or an unknown path. JSON body (sent as text/html).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2ReportsPlainErrorResponse"
            },
            "example": {
              "success": false,
              "result_data": [],
              "result_text": "404 Not Found"
            }
          }
        }
      },
      "ContactForbidden": {
        "description": "Either the account is restricted from contact-level email activity, or a valid key is used on a closed or inactive account (`This account has been closed.` or `This account is not active.`). Both are JSON bodies.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/V2ReportsPlainErrorResponse"
            },
            "examples": {
              "restricted": {
                "summary": "Restricted from contact-level email activity",
                "value": {
                  "success": false,
                  "result_text": "Contact-level email activity is not available on this account",
                  "result_data": []
                }
              },
              "closed": {
                "summary": "Closed account",
                "value": {
                  "success": false,
                  "result_data": [],
                  "result_text": "This account has been closed."
                }
              }
            }
          }
        }
      }
    }
  }
}
