{
  "openapi": "3.0.3",
  "info": {
    "title": "MobileMessage SMS API",
    "description": "The Mobile Message REST API for sending and receiving SMS in Australia. Use it to send single\nmessages or batches of up to 10000, send to a saved contact list, schedule and cancel sends, manage\ncontacts, lists and unsubscribes, register Sender IDs (including ACMA registration for custom\nalphanumeric senders), read inbound replies, and configure webhooks for real-time delivery receipts\nand inbound messages.\n\n## Base URL\n\nAll endpoints live under `https://api.mobilemessage.com.au/`.\n\n## Concurrency and throughput\n\nAn account may have up to 5 requests in flight at once. A sixth concurrent request is answered with\nHTTP 429 and the message `Too many concurrent requests. Please wait.` Wait for an in-flight request\nto finish and retry.\n\nThere is no per-second cap on submissions. Batches are accepted at up to 10000 messages per request,\nand accepted messages are queued and submitted to Australian carriers at a sustained rate of over\n400 messages per second, so a 50000-message campaign is typically fully submitted within about two\nminutes.\n\n## Errors\n\nEvery 4xx and 5xx response uses the same envelope, a JSON object with a single `error` string. There\nis no `status` field on an error response. Individual messages inside a successful batch report\ntheir own failures in `results[].status` and `results[].error` instead, so a 200 does not mean every\nmessage was accepted.\n\n## Test accounts\n\nSandbox accounts are available on request from hello@mobilemessage.com.au. A test account behaves\nexactly like a live one at the API level with sending disconnected: requests are validated and\nanswered normally with real message IDs and cost calculations, no message reaches a handset, no\ncredits are consumed, and accepted messages are recorded as delivered so lookup flows can be\nexercised end to end. Around 5 seconds after a test send is accepted, a simulated delivery receipt\nwebhook is posted to the status webhook URL with the same payload as a real one, one webhook per\nmessage part, signed like a real one when a signing secret is set. Test messages to numbers ending\nin 000 are recorded as failed and their delivery receipts report failed, so failure handling can be\nexercised; all other test messages are reported as delivered. Inbound messages and inbound webhooks\ncan be simulated with POST /v1/test-inbound (test accounts only).\n\n## Not covered here\n\nThe Simple API at `/simple/send-sms.php`, which takes all of its parameters in the query string, is\na separate single-message endpoint outside v1 and is documented at\nhttps://mobilemessage.com.au/api-documentation.",
    "version": "2.0.0",
    "contact": {
      "name": "Mobile Message Support",
      "url": "https://mobilemessage.com.au/api-documentation",
      "email": "hello@mobilemessage.com.au"
    }
  },
  "servers": [
    {
      "url": "https://api.mobilemessage.com.au",
      "description": "Production"
    }
  ],
  "security": [
    {
      "basicAuth": []
    }
  ],
  "tags": [
    {
      "name": "Messages",
      "description": "Send SMS, send to a list, look up sent messages and cancel scheduled sends."
    },
    {
      "name": "Contacts",
      "description": "Create, find, update and delete contact records used for personalisation."
    },
    {
      "name": "Contact Lists",
      "description": "Group contacts into named lists for use with Send to List."
    },
    {
      "name": "List Members",
      "description": "Add, remove and view the contacts inside a specific list."
    },
    {
      "name": "Unsubscribes",
      "description": "Manage the opt-out list that blocks numbers from receiving messages."
    },
    {
      "name": "Account",
      "description": "Read account level information such as the credit balance."
    },
    {
      "name": "Sender IDs",
      "description": "List active Sender IDs and register your own mobile number as a sender."
    },
    {
      "name": "ACMA Registration",
      "description": "Register custom alphanumeric Sender IDs with the ACMA Sender ID Register."
    },
    {
      "name": "Inbound SMS",
      "description": "Read inbound replies and opt-out messages received on your numbers."
    },
    {
      "name": "Webhooks",
      "description": "Configure the URLs that receive delivery receipts and inbound messages."
    },
    {
      "name": "Testing",
      "description": "Sandbox helpers available on test accounts only."
    }
  ],
  "paths": {
    "/v1/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "sendMessages",
        "summary": "Send SMS messages",
        "description": "Send up to 10000 SMS messages in one request. Each message is validated independently, so a batch can come back with a mix of successful and failed results, but credits for the whole batch are checked together and a batch that cannot be paid for is rejected in full with a 403.\n\nFor automated integrations, especially large batches or anything with retry logic, send an Idempotency-Key header so a retry after a network error or timeout cannot duplicate the send.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessagesRequest"
              },
              "examples": {
                "single_message": {
                  "summary": "One message",
                  "value": {
                    "messages": [
                      {
                        "to": "+61412345678",
                        "message": "Hello, this is a test message.",
                        "sender": "MySenderID"
                      }
                    ]
                  }
                },
                "multiple_messages": {
                  "summary": "Two messages with a custom reference",
                  "value": {
                    "messages": [
                      {
                        "to": "+61412345678",
                        "message": "First message content.",
                        "sender": "MySenderID"
                      },
                      {
                        "to": "+61487654321",
                        "message": "Second message content.",
                        "sender": "MySenderID",
                        "custom_ref": "Order123"
                      }
                    ]
                  }
                },
                "scheduled_unicode": {
                  "summary": "Scheduled Unicode send with a parts cap",
                  "value": {
                    "enable_unicode": true,
                    "max_parts": 3,
                    "messages": [
                      {
                        "to": "0412345678",
                        "message": "Your table is booked ✅",
                        "sender": "MySenderID",
                        "scheduled_for": "2026-04-01T09:00:00"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch processed. Inspect each entry in results to see which messages were accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMessagesResponse"
                },
                "examples": {
                  "success": {
                    "summary": "Two messages accepted",
                    "value": {
                      "status": "complete",
                      "send_id": 9271,
                      "ignore_unsubscribes": false,
                      "total_cost": 2,
                      "results": [
                        {
                          "to": "61412345678",
                          "message": "First message content.",
                          "sender": "MySenderID",
                          "status": "success",
                          "cost": 1,
                          "message_id": "123e4567-e89b-12d3-a456-426614174000",
                          "conversation": 0,
                          "scheduled_for": null
                        },
                        {
                          "to": "61487654321",
                          "message": "Second message content.",
                          "sender": "MySenderID",
                          "custom_ref": "Order123",
                          "status": "success",
                          "cost": 1,
                          "message_id": "123e4567-e89b-12d3-a456-426614174001",
                          "conversation": 0,
                          "scheduled_for": null
                        }
                      ]
                    }
                  },
                  "partial_failure": {
                    "summary": "One accepted, one blocked, one rejected",
                    "value": {
                      "status": "complete",
                      "send_id": 9272,
                      "ignore_unsubscribes": false,
                      "total_cost": 1,
                      "results": [
                        {
                          "to": "61412345678",
                          "message": "Hello.",
                          "sender": "MySenderID",
                          "status": "success",
                          "cost": 1,
                          "message_id": "123e4567-e89b-12d3-a456-426614174002",
                          "conversation": 0,
                          "scheduled_for": null
                        },
                        {
                          "to": "61487654321",
                          "message": "Hello.",
                          "sender": "MySenderID",
                          "status": "blocked",
                          "error": "The recipient has unsubscribed and cannot receive messages."
                        },
                        {
                          "to": "notanumber",
                          "message": "Hello.",
                          "sender": "MySenderID",
                          "status": "error",
                          "error": "Invalid phone number format"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "headers": {
              "Idempotency-Replay": {
                "description": "Set to true when this response was replayed from a previous request with the same Idempotency-Key.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request body or a top-level parameter is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Missing or invalid \"messages\" parameter. It should be an array of messages.",
                    "value": {
                      "error": "Missing or invalid \"messages\" parameter. It should be an array of messages."
                    }
                  },
                  "error_2": {
                    "summary": "The maximum number of messages per request is 10000.",
                    "value": {
                      "error": "The maximum number of messages per request is 10000."
                    }
                  },
                  "error_3": {
                    "summary": "\"max_parts\" must be between 1 and 99.",
                    "value": {
                      "error": "\"max_parts\" must be between 1 and 99."
                    }
                  },
                  "error_4": {
                    "summary": "Request body is not valid JSON.",
                    "value": {
                      "error": "Request body is not valid JSON."
                    }
                  },
                  "error_5": {
                    "summary": "Request body must be a JSON object.",
                    "value": {
                      "error": "Request body must be a JSON object."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The send cannot proceed for the whole batch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Insufficient credits to send the batch of messages.",
                    "value": {
                      "error": "Insufficient credits to send the batch of messages."
                    }
                  },
                  "error_2": {
                    "summary": "No allowed senders found for this user.",
                    "value": {
                      "error": "No allowed senders found for this user."
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key has already been used with a different request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Idempotency-Key was reused with a different request payload."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "500": {
            "description": "The batch could not be processed. No messages were sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Failed to process messages: <details>"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "getMessages",
        "summary": "Look up messages or browse sent history",
        "description": "Look up a specific message by message_id, look up every message carrying a custom_ref, or browse paginated outbound history when neither is supplied.\n\nThe two shapes use different field names. A message_id or custom_ref lookup returns to, message, sender and message_id, while the paginated history returns recipient_number, sender_id, message_content and message_id. A custom_ref lookup is capped at the 100 most recent matches and ignores limit and offset.",
        "parameters": [
          {
            "name": "message_id",
            "in": "query",
            "required": false,
            "description": "UUID returned when the message was sent. Takes precedence over custom_ref.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "custom_ref",
            "in": "query",
            "required": false,
            "description": "Your custom reference, matched exactly. Returns the 100 most recent matching messages.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Paginated history only. Filter by delivery status.",
            "schema": {
              "$ref": "#/components/schemas/MessageStatus"
            }
          },
          {
            "$ref": "#/components/parameters/FromDate"
          },
          {
            "$ref": "#/components/parameters/ToDate"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching messages. The body is the lookup shape when message_id or custom_ref was supplied, and the paginated shape otherwise.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/MessageLookupResponse"
                    },
                    {
                      "$ref": "#/components/schemas/MessageHistoryResponse"
                    }
                  ]
                },
                "examples": {
                  "lookup": {
                    "summary": "Lookup by message_id or custom_ref",
                    "value": {
                      "status": "complete",
                      "results": [
                        {
                          "to": "61412345678",
                          "message": "Hello, this is a test message.",
                          "sender": "MySenderID",
                          "custom_ref": "Order123",
                          "status": "delivered",
                          "cost": "1.00",
                          "message_id": "123e4567-e89b-12d3-a456-426614174000",
                          "requested_at": "2026-03-30 12:14:20",
                          "scheduled_for": null,
                          "send_id": 9271
                        }
                      ]
                    }
                  },
                  "history": {
                    "summary": "Paginated history",
                    "value": {
                      "status": "complete",
                      "total": 1284,
                      "limit": 50,
                      "offset": 0,
                      "results": [
                        {
                          "message_id": "123e4567-e89b-12d3-a456-426614174000",
                          "recipient_number": "61412345678",
                          "sender_id": "MySenderID",
                          "message_content": "Hello, this is a test message.",
                          "status": "delivered",
                          "cost": "1.00",
                          "custom_ref": "Order123",
                          "requested_at": "2026-03-30 12:14:20",
                          "scheduled_for": null,
                          "send_id": 9271
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A filter parameter is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Invalid status filter. Allowed values: pending, scheduled, sent, delivered, failed, cancelled",
                    "value": {
                      "error": "Invalid status filter. Allowed values: pending, scheduled, sent, delivered, failed, cancelled"
                    }
                  },
                  "error_2": {
                    "summary": "Invalid \"from\" date. Expected format: YYYY-MM-DD.",
                    "value": {
                      "error": "Invalid \"from\" date. Expected format: YYYY-MM-DD."
                    }
                  },
                  "error_3": {
                    "summary": "Invalid \"to\" date. Expected format: YYYY-MM-DD.",
                    "value": {
                      "error": "Invalid \"to\" date. Expected format: YYYY-MM-DD."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No message matched the message_id or custom_ref supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "No message(s) found with the given ID or reference."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Messages"
        ],
        "operationId": "cancelScheduledMessages",
        "summary": "Cancel scheduled messages",
        "description": "Cancel one or more scheduled messages and refund their credits in full. Only messages still in the scheduled state can be cancelled.\n\nSupply message_id, send_id or custom_ref. If more than one is supplied they are evaluated in that order. The response shape depends on which identifier was used.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelMessagesRequest"
              },
              "examples": {
                "by_message_id": {
                  "summary": "Cancel one message",
                  "value": {
                    "message_id": "123e4567-e89b-12d3-a456-426614174000"
                  }
                },
                "by_send_id": {
                  "summary": "Cancel a whole batch",
                  "value": {
                    "send_id": 9271
                  }
                },
                "by_custom_ref": {
                  "summary": "Cancel everything with a reference",
                  "value": {
                    "custom_ref": "Order123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The matching scheduled messages were cancelled and the credits refunded.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/CancelByMessageIdResponse"
                    },
                    {
                      "$ref": "#/components/schemas/CancelBySendIdResponse"
                    },
                    {
                      "$ref": "#/components/schemas/CancelByCustomRefResponse"
                    }
                  ]
                },
                "examples": {
                  "by_message_id": {
                    "summary": "Cancelled by message_id",
                    "value": {
                      "status": "complete",
                      "message_id": "123e4567-e89b-12d3-a456-426614174000",
                      "cancelled": true,
                      "credits_refunded": 1
                    }
                  },
                  "by_send_id": {
                    "summary": "Cancelled by send_id",
                    "value": {
                      "status": "complete",
                      "send_id": 9271,
                      "cancelled_count": 240,
                      "credits_refunded": 240
                    }
                  },
                  "by_custom_ref": {
                    "summary": "Cancelled by custom_ref",
                    "value": {
                      "status": "complete",
                      "custom_ref": "Order123",
                      "cancelled_count": 12,
                      "credits_refunded": 12
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No usable identifier was supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"message_id\", \"custom_ref\", or \"send_id\" is required.",
                    "value": {
                      "error": "\"message_id\", \"custom_ref\", or \"send_id\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Request body is not valid JSON.",
                    "value": {
                      "error": "Request body is not valid JSON."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Nothing matched, or nothing matching is still scheduled. Note that a non-scheduled match yields 404 rather than 409 for the send_id and custom_ref variants.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Message not found.",
                    "value": {
                      "error": "Message not found."
                    }
                  },
                  "error_2": {
                    "summary": "Send not found.",
                    "value": {
                      "error": "Send not found."
                    }
                  },
                  "error_3": {
                    "summary": "No scheduled messages found for this send.",
                    "value": {
                      "error": "No scheduled messages found for this send."
                    }
                  },
                  "error_4": {
                    "summary": "No scheduled messages found with the given custom_ref.",
                    "value": {
                      "error": "No scheduled messages found with the given custom_ref."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The message exists but is no longer scheduled. Only the message_id variant returns this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Message is not in a scheduled state."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "500": {
            "description": "The cancellation could not be completed and was rolled back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Failed to cancel message.",
                    "value": {
                      "error": "Failed to cancel message."
                    }
                  },
                  "error_2": {
                    "summary": "Failed to cancel send.",
                    "value": {
                      "error": "Failed to cancel send."
                    }
                  },
                  "error_3": {
                    "summary": "Failed to cancel messages.",
                    "value": {
                      "error": "Failed to cancel messages."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/list-send": {
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "sendToList",
        "summary": "Send an SMS to every contact in a list",
        "description": "Send one message to every contact in a list, with per-contact placeholder substitution, {optout} support and optional scheduling. Unsubscribed numbers are filtered out unless ignore_unsubscribes is set, and duplicate numbers are removed unless your account allows duplicate contacts.\n\nUse GET /v1/messages?custom_ref=your-ref afterwards to check per-recipient delivery status. This endpoint does not accept an Idempotency-Key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListSendRequest"
              },
              "examples": {
                "immediate": {
                  "summary": "Send now",
                  "value": {
                    "list_id": 42,
                    "sender": "MySenderID",
                    "message": "Hi {first_name}, doors open at 7pm.{optout}",
                    "custom_ref": "launch-night"
                  }
                },
                "staggered": {
                  "summary": "Scheduled and staggered over an hour",
                  "value": {
                    "list_id": 42,
                    "sender": "MySenderID",
                    "message": "Hi {first_name}, doors open at 7pm.{optout}",
                    "scheduled_for": "2026-04-01T09:00:00",
                    "stagger_minutes": 60
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The send was accepted and queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListSendResponse"
                },
                "example": {
                  "status": "complete",
                  "send_id": 9271,
                  "list_id": 42,
                  "total_recipients": 1180,
                  "total_blocked_unsubscribed": 14,
                  "total_skipped_too_long": 0,
                  "total_cost": 1180,
                  "scheduled_for": null,
                  "stagger_minutes": null,
                  "send_status": "queued"
                }
              }
            }
          },
          "400": {
            "description": "A required field is missing, or nothing is left to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"list_id\" is required.",
                    "value": {
                      "error": "\"list_id\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "\"sender\" is required.",
                    "value": {
                      "error": "\"sender\" is required."
                    }
                  },
                  "error_3": {
                    "summary": "\"message\" is required.",
                    "value": {
                      "error": "\"message\" is required."
                    }
                  },
                  "error_4": {
                    "summary": "\"max_parts\" must be between 1 and 99.",
                    "value": {
                      "error": "\"max_parts\" must be between 1 and 99."
                    }
                  },
                  "error_5": {
                    "summary": "The \"sender\" field must be a string, not an object or array.",
                    "value": {
                      "error": "The \"sender\" field must be a string, not an object or array."
                    }
                  },
                  "error_6": {
                    "summary": "scheduled_for is not a valid datetime.",
                    "value": {
                      "error": "scheduled_for is not a valid datetime."
                    }
                  },
                  "error_7": {
                    "summary": "The list has no contacts.",
                    "value": {
                      "error": "The list has no contacts."
                    }
                  },
                  "error_8": {
                    "summary": "No recipients remain after filtering unsubscribes.",
                    "value": {
                      "error": "No recipients remain after filtering unsubscribes."
                    }
                  },
                  "error_9": {
                    "summary": "No recipients remain after filtering.",
                    "value": {
                      "error": "No recipients remain after filtering."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The sender is not usable, or the account cannot pay for the send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Invalid sender. You do not have permission to use this sender.",
                    "value": {
                      "error": "Invalid sender. You do not have permission to use this sender."
                    }
                  },
                  "error_2": {
                    "summary": "Insufficient credits. Need 1180 credits, have 400.",
                    "value": {
                      "error": "Insufficient credits. Need 1180 credits, have 400."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "List not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "500": {
            "description": "The send could not be completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Failed to send to list: <details>"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only POST is allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContacts",
        "summary": "List contacts",
        "description": "List contacts with optional filters. Filters combine with AND. The number filter is an exact match after normalisation, and every other filter is a substring match.",
        "parameters": [
          {
            "name": "number",
            "in": "query",
            "required": false,
            "description": "Exact match on phone number, in Australian local or international format.",
            "schema": {
              "type": "string",
              "example": "0412345678"
            }
          },
          {
            "name": "first_name",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on first name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "last_name",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on last name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "company",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on company.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "other",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on other.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field_1",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on custom contact field 1.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field_2",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on custom contact field 2.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field_3",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on custom contact field 3.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field_4",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on custom contact field 4.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field_5",
            "in": "query",
            "required": false,
            "description": "Partial (substring) match on custom contact field 5.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching contacts, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactListResponse"
                },
                "example": {
                  "status": "complete",
                  "total": 2,
                  "limit": 50,
                  "offset": 0,
                  "results": [
                    {
                      "contact_id": 8812,
                      "number": "61412345678",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "company": "Acme",
                      "other": "",
                      "field_1": "",
                      "field_2": "",
                      "field_3": "",
                      "field_4": "",
                      "field_5": ""
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The number filter could not be parsed as an Australian mobile number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid phone number format."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "createContact",
        "summary": "Add a contact",
        "description": "Add a new contact. If duplicate contacts are disabled on your account, adding a number that already exists returns 409. Accounts that allow duplicates never see that conflict.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContactRequest"
              },
              "examples": {
                "basic": {
                  "summary": "New contact",
                  "value": {
                    "number": "0412345678",
                    "first_name": "Jane",
                    "last_name": "Smith",
                    "company": "Acme"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The contact was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateContactResponse"
                },
                "example": {
                  "status": "complete",
                  "contact_id": 8812,
                  "number": "61412345678"
                }
              }
            }
          },
          "400": {
            "description": "The request is missing or has an invalid number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"number\" is required.",
                    "value": {
                      "error": "\"number\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid phone number format.",
                    "value": {
                      "error": "Invalid phone number format."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "This number already exists in your contacts and duplicates are disabled on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This phone number already exists in your contacts."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "500": {
            "description": "The contact could not be stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Failed to add contact."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Contacts"
        ],
        "operationId": "updateContact",
        "summary": "Update a contact",
        "description": "Update one or more fields on an existing contact, including its number. Only the fields you supply are changed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContactRequest"
              },
              "examples": {
                "rename": {
                  "summary": "Change a name and a custom field",
                  "value": {
                    "contact_id": 8812,
                    "first_name": "Janet",
                    "field_1": "gold"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The contact was updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateContactResponse"
                },
                "example": {
                  "status": "complete",
                  "contact_id": 8812
                }
              }
            }
          },
          "400": {
            "description": "The request is missing contact_id, has an invalid number, or changes nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"contact_id\" is required.",
                    "value": {
                      "error": "\"contact_id\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid phone number format.",
                    "value": {
                      "error": "Invalid phone number format."
                    }
                  },
                  "error_3": {
                    "summary": "No fields provided to update.",
                    "value": {
                      "error": "No fields provided to update."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No contact with that ID exists on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Contact not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Contacts"
        ],
        "operationId": "deleteContact",
        "summary": "Delete a contact",
        "description": "Remove a contact. The contact is also removed from every list. Deleting a contact that does not exist is not an error, it returns removed set to false.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The delete was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteContactResponse"
                },
                "example": {
                  "status": "complete",
                  "contact_id": 8812,
                  "removed": true
                }
              }
            }
          },
          "400": {
            "description": "contact_id was missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "\"contact_id\" is required."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/lists": {
      "get": {
        "tags": [
          "Contact Lists"
        ],
        "operationId": "getLists",
        "summary": "List contact lists",
        "description": "Return every contact list on your account with its contact count, newest first. This endpoint is not paginated.",
        "responses": {
          "200": {
            "description": "All contact lists on the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsResponse"
                },
                "example": {
                  "status": "complete",
                  "results": [
                    {
                      "list_id": 42,
                      "name": "VIP customers",
                      "created_at": "2026-02-11 04:12:00",
                      "contact_count": 1194
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Contact Lists"
        ],
        "operationId": "createList",
        "summary": "Create a contact list",
        "description": "Create a list. This is a get-or-create: if a list with the same name already exists it is returned with existing set to true rather than raising a conflict.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The list was created, or an existing list with the same name was returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateListResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "name": "VIP customers",
                  "existing": false
                }
              }
            }
          },
          "400": {
            "description": "name was missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "\"name\" is required."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Contact Lists"
        ],
        "operationId": "renameList",
        "summary": "Rename a contact list",
        "description": "Rename a list. Note that submitting the name the list already has also returns 404, because the endpoint reports success only when a row actually changed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The list was renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenameListResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "name": "VIP customers 2026"
                }
              }
            }
          },
          "400": {
            "description": "A required field was missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"list_id\" is required.",
                    "value": {
                      "error": "\"list_id\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "\"name\" is required.",
                    "value": {
                      "error": "\"name\" is required."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No list with that ID exists on your account, or the name was unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "List not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact Lists"
        ],
        "operationId": "deleteList",
        "summary": "Delete a contact list",
        "description": "Delete a list. By default only the list and its membership records go, and the contact records are kept. Set delete_contacts to permanently delete the member contacts too.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The delete was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteListResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "removed": true
                }
              }
            }
          },
          "400": {
            "description": "list_id was missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "\"list_id\" is required."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, PATCH, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/list-contacts": {
      "get": {
        "tags": [
          "List Members"
        ],
        "operationId": "getListMembers",
        "summary": "List the contacts in a list",
        "description": "Return the members of one list, most recently added first.",
        "parameters": [
          {
            "name": "list_id",
            "in": "query",
            "required": true,
            "description": "ID of the list to query.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Members of the list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListMembersResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "total": 1194,
                  "limit": 50,
                  "offset": 0,
                  "results": [
                    {
                      "contact_id": 8812,
                      "number": "61412345678",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "company": "Acme",
                      "other": "",
                      "field_1": "",
                      "field_2": "",
                      "field_3": "",
                      "field_4": "",
                      "field_5": "",
                      "added": "2026-02-11 04:15:22"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "list_id was missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "\"list_id\" is required."
                }
              }
            }
          },
          "404": {
            "description": "No list with that ID exists on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "List not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "List Members"
        ],
        "operationId": "addListMember",
        "summary": "Add a contact to a list",
        "description": "Add an existing contact to a list. Adding a contact that is already a member is not an error, it returns added set to false.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListMemberRequest"
              },
              "examples": {
                "by_contact_id": {
                  "summary": "By contact ID",
                  "value": {
                    "list_id": 42,
                    "contact_id": 8812
                  }
                },
                "by_number": {
                  "summary": "By number",
                  "value": {
                    "list_id": 42,
                    "number": "0412345678"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The membership was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddListMemberResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "contact_id": 8812,
                  "added": true
                }
              }
            }
          },
          "400": {
            "description": "The request did not identify a list and a contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"list_id\" and either \"contact_id\" or \"number\" are required.",
                    "value": {
                      "error": "\"list_id\" and either \"contact_id\" or \"number\" are required."
                    }
                  },
                  "error_2": {
                    "summary": "\"number\" is not a valid Australian mobile number.",
                    "value": {
                      "error": "\"number\" is not a valid Australian mobile number."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list or the contact does not exist on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "List not found.",
                    "value": {
                      "error": "List not found."
                    }
                  },
                  "error_2": {
                    "summary": "Contact not found.",
                    "value": {
                      "error": "Contact not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "List Members"
        ],
        "operationId": "removeListMember",
        "summary": "Remove a contact from a list",
        "description": "Remove a contact from a list. The contact record itself is not deleted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListMemberRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The removal was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveListMemberResponse"
                },
                "example": {
                  "status": "complete",
                  "list_id": 42,
                  "contact_id": 8812,
                  "removed": true
                }
              }
            }
          },
          "400": {
            "description": "The request did not identify a list and a contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"list_id\" and either \"contact_id\" or \"number\" are required.",
                    "value": {
                      "error": "\"list_id\" and either \"contact_id\" or \"number\" are required."
                    }
                  },
                  "error_2": {
                    "summary": "\"number\" is not a valid Australian mobile number.",
                    "value": {
                      "error": "\"number\" is not a valid Australian mobile number."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The contact does not exist on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Contact not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/unsubscribes": {
      "get": {
        "tags": [
          "Unsubscribes"
        ],
        "operationId": "listUnsubscribes",
        "summary": "List unsubscribed numbers",
        "description": "Return the numbers on your opt-out list, most recently unsubscribed first.",
        "parameters": [
          {
            "name": "number",
            "in": "query",
            "required": false,
            "description": "Filter to a single phone number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Unsubscribed numbers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsubscribesResponse"
                },
                "example": {
                  "status": "complete",
                  "total": 41,
                  "limit": 50,
                  "offset": 0,
                  "results": [
                    {
                      "number": "61412345678",
                      "updated_at": "2026-03-02 22:41:09"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The number filter could not be parsed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid phone number format."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Unsubscribes"
        ],
        "operationId": "addUnsubscribe",
        "summary": "Unsubscribe a number",
        "description": "Add a number to your opt-out list. This also cancels any scheduled messages to that number, refunding them, and removes the matching contact from every list.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnsubscribeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The number is on your unsubscribe list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddUnsubscribeResponse"
                },
                "example": {
                  "status": "complete",
                  "number": "61412345678",
                  "added": true
                }
              }
            }
          },
          "400": {
            "description": "The number was missing or unparseable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"number\" is required.",
                    "value": {
                      "error": "\"number\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid phone number format.",
                    "value": {
                      "error": "Invalid phone number format."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Unsubscribes"
        ],
        "operationId": "removeUnsubscribe",
        "summary": "Resubscribe a number",
        "description": "Remove a number from your opt-out list so it can receive messages again.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnsubscribeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The removal was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveUnsubscribeResponse"
                },
                "example": {
                  "status": "complete",
                  "number": "61412345678",
                  "removed": true
                }
              }
            }
          },
          "400": {
            "description": "The number was missing or unparseable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"number\" is required.",
                    "value": {
                      "error": "\"number\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid phone number format.",
                    "value": {
                      "error": "Invalid phone number format."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccountBalance",
        "summary": "Get your credit balance",
        "description": "Return the SMS credit balance for the authenticated account. No parameters are required.",
        "responses": {
          "200": {
            "description": "Your current credit balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountBalanceResponse"
                },
                "example": {
                  "status": "complete",
                  "credit_balance": 9981
                }
              }
            }
          },
          "404": {
            "description": "The account could not be read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Account not found or no credit balance available."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET is allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/senders": {
      "get": {
        "tags": [
          "Sender IDs"
        ],
        "operationId": "listSenders",
        "summary": "List your Sender IDs",
        "description": "Return every active Sender ID on your account, default sender first.",
        "responses": {
          "200": {
            "description": "Active Sender IDs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendersResponse"
                },
                "example": {
                  "status": "complete",
                  "results": [
                    {
                      "sender": "MySenderID",
                      "type": "alpha",
                      "label": "Marketing",
                      "is_default": true
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET and POST are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Sender IDs"
        ],
        "operationId": "registerOwnNumberSender",
        "summary": "Register your own mobile number as a sender",
        "description": "Send a free verification SMS to a mobile number. The owner must click the link in it before the number becomes an active sender on your account. A maximum of 3 verifications can be pending at once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterSenderRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A verification link was sent to the number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegisterSenderResponse"
                },
                "example": {
                  "status": "verification_sent",
                  "message": "A verification link has been sent to the number."
                }
              }
            }
          },
          "400": {
            "description": "The number was missing or unparseable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"number\" is required.",
                    "value": {
                      "error": "\"number\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid phone number format.",
                    "value": {
                      "error": "Invalid phone number format."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The number is already an active sender on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This number is already an active sender on your account."
                }
              }
            }
          },
          "429": {
            "description": "Too many pending verifications, or a verification was already sent to this number in the last 5 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "You have too many pending verifications. Please wait for existing ones to complete or expire.",
                    "value": {
                      "error": "You have too many pending verifications. Please wait for existing ones to complete or expire."
                    }
                  },
                  "error_2": {
                    "summary": "A verification was already sent to this number recently. Please wait 5 minutes before retrying.",
                    "value": {
                      "error": "A verification was already sent to this number recently. Please wait 5 minutes before retrying."
                    }
                  },
                  "error_3": {
                    "summary": "Too many concurrent requests. Please wait.",
                    "value": {
                      "error": "Too many concurrent requests. Please wait."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET and POST are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/acma-registration": {
      "get": {
        "tags": [
          "ACMA Registration"
        ],
        "operationId": "getAcmaRegistrations",
        "summary": "List ACMA registrations, brands and partners",
        "description": "Without parameters, return every custom alphanumeric Sender ID registration on your account together with your saved brands and on-behalf-of partners, so you can reuse a brand_id or partner_id on a later submission. Supply the sender parameter to get the ACMA status of one Sender ID instead. Only alphanumeric senders are covered.",
        "parameters": [
          {
            "name": "sender",
            "in": "query",
            "required": false,
            "description": "Return the ACMA registration status for this Sender ID instead of the full listing.",
            "schema": {
              "type": "string"
            },
            "example": "MyBrand"
          }
        ],
        "responses": {
          "200": {
            "description": "The full listing, or the status of the requested Sender ID.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AcmaListingResponse"
                    },
                    {
                      "$ref": "#/components/schemas/AcmaSenderStatusResponse"
                    }
                  ]
                },
                "examples": {
                  "listing": {
                    "summary": "Full listing",
                    "value": {
                      "registrations": [
                        {
                          "sender_id": 3312,
                          "sender": "MyBrand",
                          "status": "active",
                          "acma_status": "verified",
                          "brand_id": 88,
                          "partner_id": null
                        }
                      ],
                      "brands": [
                        {
                          "id": 88,
                          "brand_name": "Acme Pty Ltd",
                          "abn": "51824753556",
                          "website": "https://acme.com.au"
                        }
                      ],
                      "partners": []
                    }
                  },
                  "sender_lookup": {
                    "summary": "Single sender lookup",
                    "value": {
                      "sender": "MyBrand",
                      "sender_id": 3312,
                      "acma_status": "verified",
                      "brand_id": 88,
                      "chatbot_id": 904,
                      "partner_id": null
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The Sender ID is not an alphanumeric sender on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Sender not found."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET and POST are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "ACMA Registration"
        ],
        "operationId": "submitAcmaRegistration",
        "summary": "Submit a Sender ID for ACMA registration",
        "description": "Register a custom alphanumeric Sender ID with the Australian Communications and Media Authority. Every custom Sender ID used in Australia must be on the ACMA Sender ID Register. Submissions are reviewed by Mobile Message before being forwarded to ACMA.\n\nIf you only need to register a handful of Sender IDs it is usually easier to use the guided form at https://app.mobilemessage.com.au/. This endpoint suits businesses registering Sender IDs at scale, such as agencies managing multiple clients.\n\nA new brand whose ABN matches a brand already saved on your account reuses that brand rather than creating a second one, and the same applies to partners matched on applicant_abn.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcmaRegistrationRequest"
              },
              "examples": {
                "own_brand": {
                  "summary": "Register for your own business",
                  "value": {
                    "sender": "MyBrand",
                    "sender_id_relation_type": "registered_company_name",
                    "contact_first_name": "Jane",
                    "contact_last_name": "Smith",
                    "contact_email": "jane@acme.com.au",
                    "abn": "51 824 753 556",
                    "business_name": "Acme Pty Ltd",
                    "address_line1": "1 Example St",
                    "suburb": "Sydney",
                    "state": "NSW",
                    "postcode": "2000",
                    "website": "https://acme.com.au",
                    "business_phone": "0299999999"
                  }
                },
                "existing_brand": {
                  "summary": "Reuse a saved brand",
                  "value": {
                    "sender": "MyBrand2",
                    "sender_id_relation_type": "registered_trade_mark",
                    "sender_id_relation_detail": "MYBRAND",
                    "brand_mode": "existing",
                    "brand_id": 88,
                    "contact_first_name": "Jane",
                    "contact_last_name": "Smith",
                    "contact_email": "jane@acme.com.au"
                  }
                },
                "on_behalf": {
                  "summary": "Register on behalf of a client",
                  "value": {
                    "sender": "PizzaCo",
                    "sender_id_relation_type": "registered_business_name",
                    "sender_id_relation_detail": "Pizza Company Pty Ltd",
                    "contact_first_name": "Sam",
                    "contact_last_name": "Lee",
                    "contact_email": "sam@pizzaco.com.au",
                    "abn": "12345678901",
                    "business_name": "Pizza Company Pty Ltd",
                    "address_line1": "9 Dough Rd",
                    "suburb": "Melbourne",
                    "state": "VIC",
                    "postcode": "3000",
                    "website": "https://pizzaco.com.au",
                    "business_phone": "0399999999",
                    "on_behalf": true,
                    "applicant_abn": "51824753556",
                    "applicant_name": "Acme Marketing",
                    "applicant_contact_first_name": "Jane",
                    "applicant_contact_last_name": "Smith",
                    "applicant_contact_email": "jane@acme.com.au",
                    "applicant_website": "https://acme.com.au",
                    "applicant_address_line1": "1 Example St",
                    "applicant_suburb": "Sydney",
                    "applicant_state": "NSW",
                    "applicant_postcode": "2000"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The registration was submitted and is awaiting internal review.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcmaRegistrationResponse"
                },
                "example": {
                  "status": "complete",
                  "message": "Sender ID registration submitted and is pending review.",
                  "sender_id": 3312,
                  "brand_id": 88,
                  "chatbot_id": 904,
                  "partner_id": null,
                  "acma_status": "pending_internal_review"
                }
              }
            }
          },
          "400": {
            "description": "A required field is missing or invalid. The error names the field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"sender\" is required.",
                    "value": {
                      "error": "\"sender\" is required."
                    }
                  },
                  "error_2": {
                    "summary": "Sender ID must be between 1 and 11 characters.",
                    "value": {
                      "error": "Sender ID must be between 1 and 11 characters."
                    }
                  },
                  "error_3": {
                    "summary": "Sender ID can only contain standard printable characters.",
                    "value": {
                      "error": "Sender ID can only contain standard printable characters."
                    }
                  },
                  "error_4": {
                    "summary": "Sender ID cannot be only numbers.",
                    "value": {
                      "error": "Sender ID cannot be only numbers."
                    }
                  },
                  "error_5": {
                    "summary": "\"sender_id_relation_type\" must be one of: registered_company_name, registered_business_name, registered_trade_mark, registered_domain_name",
                    "value": {
                      "error": "\"sender_id_relation_type\" must be one of: registered_company_name, registered_business_name, registered_trade_mark, registered_domain_name"
                    }
                  },
                  "error_6": {
                    "summary": "\"sender_id_relation_detail\" is required for this relation type.",
                    "value": {
                      "error": "\"sender_id_relation_detail\" is required for this relation type."
                    }
                  },
                  "error_7": {
                    "summary": "\"contact_first_name\", \"contact_last_name\" and \"contact_email\" are required.",
                    "value": {
                      "error": "\"contact_first_name\", \"contact_last_name\" and \"contact_email\" are required."
                    }
                  },
                  "error_8": {
                    "summary": "\"brand_id\" is required when brand_mode is \"existing\".",
                    "value": {
                      "error": "\"brand_id\" is required when brand_mode is \"existing\"."
                    }
                  },
                  "error_9": {
                    "summary": "\"abn\" must be exactly 11 digits.",
                    "value": {
                      "error": "\"abn\" must be exactly 11 digits."
                    }
                  },
                  "error_10": {
                    "summary": "\"state\" must be one of: ACT, NSW, NT, QLD, SA, TAS, VIC, WA",
                    "value": {
                      "error": "\"state\" must be one of: ACT, NSW, NT, QLD, SA, TAS, VIC, WA"
                    }
                  },
                  "error_11": {
                    "summary": "\"applicant_name\" is required when on_behalf is true.",
                    "value": {
                      "error": "\"applicant_name\" is required when on_behalf is true."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "A referenced record does not exist on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Sender not found for this account.",
                    "value": {
                      "error": "Sender not found for this account."
                    }
                  },
                  "error_2": {
                    "summary": "Selected brand not found for this account.",
                    "value": {
                      "error": "Selected brand not found for this account."
                    }
                  },
                  "error_3": {
                    "summary": "Selected partner not found for this account.",
                    "value": {
                      "error": "Selected partner not found for this account."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "This Sender ID already has a registration that is verified or in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "This Sender ID is already registered or in progress with ACMA. Please contact support if you need to make changes."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "500": {
            "description": "The submission could not be completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "We could not submit this Sender ID right now. Please try again or contact support."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET and POST are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/inbound": {
      "get": {
        "tags": [
          "Inbound SMS"
        ],
        "operationId": "listInboundMessages",
        "summary": "List inbound messages",
        "description": "Return inbound SMS replies and opt-out messages received on your numbers, newest first. For real-time notification, configure an inbound webhook instead.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FromDate"
          },
          {
            "$ref": "#/components/parameters/ToDate"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Inbound messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundResponse"
                },
                "example": {
                  "status": "complete",
                  "total": 87,
                  "limit": 50,
                  "offset": 0,
                  "results": [
                    {
                      "message_id": "123e4567-e89b-12d3-a456-426614174000",
                      "from": "61412345678",
                      "to": "61480000000",
                      "message": "YES",
                      "type": "inbound",
                      "received_at": "2026-03-30 12:20:11"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A date filter is not in YYYY-MM-DD format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Invalid \"from\" date. Expected format: YYYY-MM-DD.",
                    "value": {
                      "error": "Invalid \"from\" date. Expected format: YYYY-MM-DD."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid \"to\" date. Expected format: YYYY-MM-DD.",
                    "value": {
                      "error": "Invalid \"to\" date. Expected format: YYYY-MM-DD."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET is allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhooks",
        "summary": "Get your webhook URLs",
        "description": "Return the inbound and status webhook URLs configured on your account, and whether webhook signing is switched on.\n\nWebhook signing is optional and is switched on by generating a signing secret under Settings then API in the dashboard. Once a secret exists, every webhook POST carries X-MM-Timestamp, the unix timestamp in seconds at the moment of signing, and X-MM-Signature, the lowercase hex HMAC-SHA256 of `{timestamp}.{raw_body}` keyed with the secret. Capture the raw body before parsing, because re-encoding the JSON changes the signature. Compare with a timing-safe comparison and reject any request whose timestamp is more than 5 minutes from your own clock. Accounts with no secret keep receiving webhooks with no extra headers.",
        "responses": {
          "200": {
            "description": "Your webhook configuration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhooksResponse"
                },
                "example": {
                  "status": "complete",
                  "webhooks": {
                    "inbound": "https://example.com/hooks/inbound",
                    "status": null
                  },
                  "has_signing_secret": true
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "setWebhook",
        "summary": "Set a webhook URL",
        "description": "Register the URL that will receive POST notifications for a webhook type. Set type to \"inbound\" to receive replies and opt-outs, described by the WebhookInbound schema, or \"status\" to receive delivery receipts, described by the WebhookDeliveryReceipt schema. Both payloads are shown as callbacks on this operation.\n\nURLs are limited to 1024 characters, and hosts that resolve to private or internal addresses are rejected.\n\nWebhook signing is optional and is switched on by generating a signing secret under Settings then API in the dashboard. Once a secret exists, every webhook POST carries X-MM-Timestamp, the unix timestamp in seconds at the moment of signing, and X-MM-Signature, the lowercase hex HMAC-SHA256 of `{timestamp}.{raw_body}` keyed with the secret. Capture the raw body before parsing, because re-encoding the JSON changes the signature. Compare with a timing-safe comparison and reject any request whose timestamp is more than 5 minutes from your own clock. Accounts with no secret keep receiving webhooks with no extra headers.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetWebhookRequest"
              }
            }
          }
        },
        "callbacks": {
          "inboundMessage": {
            "{$request.body#/url}": {
              "post": {
                "operationId": "webhookInboundDelivery",
                "summary": "Inbound message or opt-out notification",
                "description": "Sent to the URL registered with type \"inbound\".",
                "parameters": [
                  {
                    "name": "X-MM-Timestamp",
                    "in": "header",
                    "required": false,
                    "description": "Unix timestamp in seconds at the moment the request was signed. Present only when a webhook signing secret is set.",
                    "schema": {
                      "type": "string"
                    },
                    "example": "1754640000"
                  },
                  {
                    "name": "X-MM-Signature",
                    "in": "header",
                    "required": false,
                    "description": "Lowercase hex HMAC-SHA256 of `{timestamp}.{raw_body}` keyed with your signing secret. Present only when a webhook signing secret is set.",
                    "schema": {
                      "type": "string"
                    },
                    "example": "52344b9592722e0241d82036e0920f4286bc0d47ba4624c5a1588193490a1efb"
                  }
                ],
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/WebhookInbound"
                      }
                    }
                  }
                },
                "responses": {
                  "2XX": {
                    "description": "Any 2xx marks the webhook delivered. Respond quickly and process asynchronously, because a response slower than 5 seconds counts as a failed attempt and is retried."
                  },
                  "4XX": {
                    "description": "Any non-2xx schedules a retry. Up to 10 attempts are made per event before it is marked failed."
                  }
                }
              }
            }
          },
          "deliveryReceipt": {
            "{$request.body#/url}": {
              "post": {
                "operationId": "webhookStatusDelivery",
                "summary": "Delivery receipt notification",
                "description": "Sent to the URL registered with type \"status\".",
                "parameters": [
                  {
                    "name": "X-MM-Timestamp",
                    "in": "header",
                    "required": false,
                    "description": "Unix timestamp in seconds at the moment the request was signed. Present only when a webhook signing secret is set.",
                    "schema": {
                      "type": "string"
                    },
                    "example": "1754640000"
                  },
                  {
                    "name": "X-MM-Signature",
                    "in": "header",
                    "required": false,
                    "description": "Lowercase hex HMAC-SHA256 of `{timestamp}.{raw_body}` keyed with your signing secret. Present only when a webhook signing secret is set.",
                    "schema": {
                      "type": "string"
                    },
                    "example": "52344b9592722e0241d82036e0920f4286bc0d47ba4624c5a1588193490a1efb"
                  }
                ],
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/WebhookDeliveryReceipt"
                      }
                    }
                  }
                },
                "responses": {
                  "2XX": {
                    "description": "Any 2xx marks the webhook delivered. Respond quickly and process asynchronously, because a response slower than 5 seconds counts as a failed attempt and is retried."
                  },
                  "4XX": {
                    "description": "Any non-2xx schedules a retry. Up to 10 attempts are made per event before it is marked failed."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook URL was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetWebhookResponse"
                },
                "example": {
                  "status": "complete",
                  "message": "Webhook subscribed successfully",
                  "type": "inbound",
                  "url": "https://example.com/hooks/inbound"
                }
              }
            }
          },
          "400": {
            "description": "The type or URL is missing or unusable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "Both \"url\" and \"type\" are required for subscribing.",
                    "value": {
                      "error": "Both \"url\" and \"type\" are required for subscribing."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid \"type\" value. It must be either \"inbound\" or \"status\".",
                    "value": {
                      "error": "Invalid \"type\" value. It must be either \"inbound\" or \"status\"."
                    }
                  },
                  "error_3": {
                    "summary": "Webhook URL exceeds the 1024-character limit.",
                    "value": {
                      "error": "Webhook URL exceeds the 1024-character limit."
                    }
                  },
                  "error_4": {
                    "summary": "Webhook URL must start with http:// or https://.",
                    "value": {
                      "error": "Webhook URL must start with http:// or https://."
                    }
                  },
                  "error_5": {
                    "summary": "Webhook host resolves to a private or internal address (10.0.0.1) and cannot be used.",
                    "value": {
                      "error": "Webhook host resolves to a private or internal address (10.0.0.1) and cannot be used."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Remove a webhook URL",
        "description": "Clear the URL for one webhook type. Deliveries of that type stop immediately.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook URL was cleared.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteWebhookResponse"
                },
                "example": {
                  "status": "complete",
                  "message": "Webhook unsubscribed successfully",
                  "type": "inbound"
                }
              }
            }
          },
          "400": {
            "description": "The type is missing or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "error_1": {
                    "summary": "\"type\" is required for unsubscribing.",
                    "value": {
                      "error": "\"type\" is required for unsubscribing."
                    }
                  },
                  "error_2": {
                    "summary": "Invalid \"type\" value. It must be either \"inbound\" or \"status\".",
                    "value": {
                      "error": "Invalid \"type\" value. It must be either \"inbound\" or \"status\"."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyConcurrentRequests"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "description": "Invalid request method for this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid request method. Only GET, POST, and DELETE are allowed."
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/test-inbound": {
      "post": {
        "tags": [
          "Testing"
        ],
        "operationId": "simulateInbound",
        "summary": "Simulate an inbound reply (test accounts only)",
        "description": "Simulates a reply arriving on the account. The inbound message is recorded exactly as a real reply would be, including opt-out processing when the message starts with STOP, and the account's inbound webhook URL is called if one is set with the same payload as a real inbound message. Returns 403 on live accounts. Allows up to 100 simulated inbound messages per rolling hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestInboundRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The simulated inbound message was recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestInboundResponse"
                }
              }
            }
          },
          "400": {
            "description": "A required field is missing or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account is not a test account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The hourly limit of 100 simulated inbound messages was reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic authentication using the API username and password created under Settings then API in the dashboard. Combine them as username:password, Base64 encode the result, and send it as `Authorization: Basic {base64_encoded_credentials}`. An account can hold several active keys at once, so a key can be rotated with no downtime by creating a new one, switching traffic, and then deleting the old one."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Results per page. Values are clamped into the 1 to 200 range rather than rejected, so a limit above 200 returns 200 and a limit below 1 returns 1.",
        "schema": {
          "type": "integer",
          "default": 50,
          "minimum": 1,
          "maximum": 200
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "Pagination offset. Negative values are clamped to 0.",
        "schema": {
          "type": "integer",
          "default": 0,
          "minimum": 0
        }
      },
      "FromDate": {
        "name": "from",
        "in": "query",
        "required": false,
        "description": "Only include records dated on or after this UTC date.",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-01-01"
        }
      },
      "ToDate": {
        "name": "to",
        "in": "query",
        "required": false,
        "description": "Only include records dated strictly before this UTC date. The bound is exclusive.",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-02-01"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional key that makes a retried send safe. Keys are scoped to the API key that sent them and truncated to 255 characters. Replaying the same key with a byte-identical request body returns the original response verbatim, with the header Idempotency-Replay set to true. Replaying it with a different body returns 422. Stored keys are pruned after 24 hours, so the practical replay window is one day.",
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "example": "order-4821-attempt-1"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication failed. The credentials are unknown or wrong, or the account is not active.",
        "headers": {
          "WWW-Authenticate": {
            "description": "Always `Basic realm=\"Mobile Message API\"`.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Unauthorized"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "The API could not reach its database. Safe to retry.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying. Always 300.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ServiceUnavailableError"
            },
            "example": {
              "error": "Service temporarily unavailable. Please retry shortly.",
              "status_page": "https://status.mobilemessage.com.au/"
            }
          }
        }
      },
      "TooManyConcurrentRequests": {
        "description": "Your account already has 5 requests in flight. Wait for one to finish and retry. This is a concurrency limit, not a rate limit: there is no per-second cap on submissions.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Too many concurrent requests. Please wait."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Standard error envelope. Every 4xx and 5xx response uses this shape.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human readable description of the problem."
          }
        }
      },
      "ServiceUnavailableError": {
        "type": "object",
        "description": "Returned when the API cannot reach its database. Safe to retry.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "example": "Service temporarily unavailable. Please retry shortly."
          },
          "status_page": {
            "type": "string",
            "format": "uri",
            "example": "https://status.mobilemessage.com.au/"
          }
        }
      },
      "MessageStatus": {
        "type": "string",
        "description": "Delivery status of an outbound message.",
        "enum": [
          "pending",
          "scheduled",
          "sent",
          "delivered",
          "failed",
          "cancelled"
        ]
      },
      "MessageInput": {
        "type": "object",
        "required": [
          "to",
          "message",
          "sender"
        ],
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient phone number, in Australian local format (0412345678) or international format (61412345678 or +61412345678). Normalised to 614XXXXXXXX before sending.",
            "example": "+61412345678"
          },
          "message": {
            "type": "string",
            "description": "Message content. Up to 10 parts by default, or up to max_parts. GSM-7 allows 1530 septets (10 x 153) and UCS-2 allows 670 characters (10 x 67). If Unicode is not enabled, non-GSM characters are stripped rather than rejected. Supports the placeholders {first_name}, {last_name}, {company}, {other}, {field_1} to {field_5} (or your configured field-name slugs) and {optout}. Contact placeholders are filled from the saved contact record matching the recipient number, or replaced with an empty string when no contact matches.",
            "example": "Hello {first_name}, your order is ready.{optout}"
          },
          "sender": {
            "type": "string",
            "description": "The mobile number or business name the message comes from. Must be an active Sender ID on your account. Use GET /v1/senders to list them. If the Sender ID exists on your account but is not active, an active dedicated or shared sender is substituted and the substituted value is echoed back in the result.",
            "example": "MySenderID"
          },
          "custom_ref": {
            "type": "string",
            "description": "Your own reference for this message. Returned on delivery receipts and inbound replies, and usable with GET /v1/messages?custom_ref= and DELETE /v1/messages.",
            "example": "Order123"
          },
          "unicode": {
            "type": "boolean",
            "description": "Overrides the top-level enable_unicode for this message. Any truthy JSON value enables UCS-2, including the strings \"false\" and \"no\", because this field is cast to boolean rather than parsed as a flag."
          },
          "scheduled_for": {
            "type": "string",
            "description": "UTC datetime to send this message, ISO 8601 or \"YYYY-MM-DD HH:MM:SS\". Must be more than 60 seconds in the future. A value at or before that threshold, including a past datetime, is silently downgraded to an immediate send and comes back as null.",
            "example": "2026-04-01T09:00:00"
          }
        }
      },
      "SendMessagesRequest": {
        "type": "object",
        "required": [
          "messages"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "description": "One or more message objects. Maximum 10000 per request.",
            "items": {
              "$ref": "#/components/schemas/MessageInput"
            }
          },
          "enable_unicode": {
            "type": "boolean",
            "default": false,
            "description": "When true, messages that need UCS-2 (emoji or other non-GSM characters) are sent as UCS-2. When false, non-GSM characters are stripped. Any truthy JSON value enables it, including the strings \"false\" and \"no\", because this field is cast to boolean rather than parsed as a flag. Individual messages can override it with \"unicode\"."
          },
          "max_parts": {
            "type": "integer",
            "default": 10,
            "minimum": 1,
            "maximum": 99,
            "description": "Maximum SMS parts (credits) per message, applied to every message in the batch. Messages that exceed it are skipped with status \"error\"."
          },
          "ignore_unsubscribes": {
            "type": "boolean",
            "default": false,
            "description": "Set to true to bypass unsubscribe blocking for this request. Accepts true, 1, \"1\", \"true\", \"yes\" or \"on\". Use with caution: bypassing your unsubscribe list can generate spam complaints."
          }
        }
      },
      "MessageResult": {
        "type": "object",
        "description": "Outcome for one message in the batch. Three shapes are possible. A successful message carries to, message, sender, status, cost, message_id, conversation and scheduled_for. A message that failed validation after being parsed carries to, message, sender, custom_ref, status and error. A structurally invalid entry (not an object, or a field sent as an object or array) carries only status, error and item, with no to, message or sender at all.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient number, normalised to 614XXXXXXXX on success or echoed as supplied on failure."
          },
          "message": {
            "type": "string",
            "description": "Message content after placeholder substitution and character cleaning."
          },
          "sender": {
            "type": "string",
            "description": "Sender ID actually used, which may differ from the requested one if a fallback was substituted."
          },
          "custom_ref": {
            "type": "string",
            "description": "Your custom reference, when one was supplied."
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "blocked"
            ],
            "description": "\"success\" when accepted, \"error\" on validation failure, \"blocked\" when the recipient has unsubscribed."
          },
          "cost": {
            "type": "integer",
            "description": "Credits consumed for this message. Present on success only."
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID for this message. Present on success only. Each recipient gets its own message_id."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "gsm7",
              "ucs2"
            ],
            "description": "Encoding used. Present only when Unicode was enabled for this message."
          },
          "conversation": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "Internal conversation flag. Always present on success."
          },
          "scheduled_for": {
            "type": "string",
            "nullable": true,
            "description": "UTC datetime the message is scheduled for, as \"YYYY-MM-DD HH:MM:SS\", or null for an immediate send. Present on success only."
          },
          "error": {
            "type": "string",
            "description": "Reason the message was rejected or blocked. Present on failure only."
          },
          "item": {
            "type": "integer",
            "description": "Zero-based index of the offending entry in your messages array. Present only for structurally invalid entries."
          }
        }
      },
      "SendMessagesResponse": {
        "type": "object",
        "required": [
          "status",
          "ignore_unsubscribes",
          "total_cost",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ],
            "description": "Always \"complete\" once the batch has been processed."
          },
          "send_id": {
            "type": "integer",
            "description": "Identifier for this send batch, usable with DELETE /v1/messages to cancel the whole batch. Only present when the batch contained two or more valid messages."
          },
          "ignore_unsubscribes": {
            "type": "boolean",
            "description": "Whether unsubscribe blocking was bypassed for this request."
          },
          "total_cost": {
            "type": "integer",
            "description": "Total credits consumed across the batch."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageResult"
            },
            "description": "One entry per message, in the order supplied."
          }
        }
      },
      "ListSendRequest": {
        "type": "object",
        "required": [
          "list_id",
          "sender",
          "message"
        ],
        "properties": {
          "list_id": {
            "type": "integer",
            "description": "ID of the list to send to. Must belong to your account.",
            "example": 42
          },
          "sender": {
            "type": "string",
            "description": "An active Sender ID on your account. Use GET /v1/senders to list them.",
            "example": "MySenderID"
          },
          "message": {
            "type": "string",
            "description": "Message content. Supports {first_name}, {last_name}, {company}, {other}, {field_1} to {field_5} (or your configured field-name slugs) and {optout}. Each contact's details are substituted individually.",
            "example": "Hi {first_name}, doors open at 7pm.{optout}"
          },
          "custom_ref": {
            "type": "string",
            "description": "Reference stored against every message in this send."
          },
          "enable_unicode": {
            "type": "boolean",
            "default": false,
            "description": "When true, messages needing UCS-2 are sent as UCS-2. When false, non-GSM characters are stripped."
          },
          "max_parts": {
            "type": "integer",
            "default": 10,
            "minimum": 1,
            "maximum": 99,
            "description": "Maximum SMS parts (credits) per message. Messages that exceed it are skipped and counted in total_skipped_too_long."
          },
          "ignore_unsubscribes": {
            "type": "boolean",
            "default": false,
            "description": "Bypass unsubscribe filtering. Accepts true, 1, \"1\", \"true\", \"yes\" or \"on\"."
          },
          "scheduled_for": {
            "type": "string",
            "description": "UTC datetime to send, ISO 8601 or \"YYYY-MM-DD HH:MM:SS\". Must be more than 60 seconds in the future. A value at or before that threshold, including a past datetime, is silently downgraded to an immediate send.",
            "example": "2026-04-01T09:00:00"
          },
          "stagger_minutes": {
            "type": "integer",
            "description": "Spread the send over this many minutes. Only applied when the send has 50 or more recipients after filtering. Below that it is silently ignored rather than rejected. Can be combined with scheduled_for to stagger from a future time."
          }
        }
      },
      "ListSendResponse": {
        "type": "object",
        "required": [
          "status",
          "send_id",
          "list_id",
          "total_recipients",
          "total_blocked_unsubscribed",
          "total_skipped_too_long",
          "total_cost",
          "scheduled_for",
          "stagger_minutes",
          "send_status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "send_id": {
            "type": "integer",
            "description": "Identifier for this send batch, usable with DELETE /v1/messages."
          },
          "list_id": {
            "type": "integer",
            "description": "The list that was sent to."
          },
          "total_recipients": {
            "type": "integer",
            "description": "Messages queued, after de-duplication, unsubscribe filtering and max_parts skipping."
          },
          "total_blocked_unsubscribed": {
            "type": "integer",
            "description": "Recipients blocked because they are unsubscribed."
          },
          "total_skipped_too_long": {
            "type": "integer",
            "description": "Messages skipped for exceeding max_parts."
          },
          "total_cost": {
            "type": "integer",
            "description": "Total credits consumed."
          },
          "scheduled_for": {
            "type": "string",
            "nullable": true,
            "description": "The scheduled datetime as \"YYYY-MM-DD HH:MM:SS\", or null for an immediate send."
          },
          "stagger_minutes": {
            "type": "integer",
            "nullable": true,
            "description": "Minutes the send is spread over, or null when the send was not staggered."
          },
          "send_status": {
            "type": "string",
            "enum": [
              "queued",
              "scheduled",
              "staggered"
            ],
            "description": "\"queued\" for an immediate send, \"scheduled\" for a future send, \"staggered\" when the send is spread over stagger_minutes."
          }
        }
      },
      "MessageLookupResult": {
        "type": "object",
        "description": "A message as returned by a message_id or custom_ref lookup.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient number in international format."
          },
          "message": {
            "type": "string",
            "description": "Message text as sent."
          },
          "sender": {
            "type": "string",
            "description": "Sender ID used."
          },
          "custom_ref": {
            "type": "string",
            "nullable": true,
            "description": "Your custom reference, or null."
          },
          "status": {
            "type": "string",
            "description": "Delivery status, normally one of pending, scheduled, sent, delivered, failed or cancelled."
          },
          "cost": {
            "type": "string",
            "description": "Credits consumed, returned as a decimal string such as \"1.00\".",
            "example": "1.00"
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the message."
          },
          "requested_at": {
            "type": "string",
            "description": "UTC timestamp the message was requested, as \"YYYY-MM-DD HH:MM:SS\"."
          },
          "scheduled_for": {
            "type": "string",
            "nullable": true,
            "description": "Scheduled delivery time as \"YYYY-MM-DD HH:MM:SS\", or null."
          },
          "send_id": {
            "type": "integer",
            "description": "The send batch ID. Only present when the message belongs to a batch."
          }
        }
      },
      "MessageLookupResponse": {
        "type": "object",
        "required": [
          "status",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageLookupResult"
            }
          }
        }
      },
      "MessageHistoryResult": {
        "type": "object",
        "description": "A message as returned by the paginated history. Note that the field names differ from the message_id and custom_ref lookup shape.",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the message."
          },
          "recipient_number": {
            "type": "string",
            "description": "Recipient number in international format."
          },
          "sender_id": {
            "type": "string",
            "description": "Sender ID used."
          },
          "message_content": {
            "type": "string",
            "description": "Message text as sent."
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "cost": {
            "type": "string",
            "description": "Credits consumed, returned as a decimal string such as \"1.00\".",
            "example": "1.00"
          },
          "custom_ref": {
            "type": "string",
            "nullable": true,
            "description": "Your custom reference, or null."
          },
          "requested_at": {
            "type": "string",
            "description": "UTC timestamp the message was requested, as \"YYYY-MM-DD HH:MM:SS\"."
          },
          "scheduled_for": {
            "type": "string",
            "nullable": true,
            "description": "Scheduled delivery time, or null."
          },
          "send_id": {
            "type": "integer",
            "description": "The send batch ID. Only present when the message belongs to a batch."
          }
        }
      },
      "MessageHistoryResponse": {
        "type": "object",
        "required": [
          "status",
          "total",
          "limit",
          "offset",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "total": {
            "type": "integer",
            "description": "Total messages matching the filters."
          },
          "limit": {
            "type": "integer",
            "description": "Page size actually applied after clamping."
          },
          "offset": {
            "type": "integer",
            "description": "Offset actually applied."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageHistoryResult"
            }
          }
        }
      },
      "CancelMessagesRequest": {
        "type": "object",
        "description": "Supply exactly one of message_id, send_id or custom_ref. If more than one is supplied they are evaluated in the order message_id, send_id, custom_ref.",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of a single scheduled message to cancel."
          },
          "custom_ref": {
            "type": "string",
            "description": "Cancel every scheduled message carrying this custom reference."
          },
          "send_id": {
            "type": "integer",
            "description": "Cancel every scheduled message in this send batch."
          }
        }
      },
      "CancelByMessageIdResponse": {
        "type": "object",
        "required": [
          "status",
          "message_id",
          "cancelled",
          "credits_refunded"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "The cancelled message UUID."
          },
          "cancelled": {
            "type": "boolean",
            "description": "Always true on a successful cancellation."
          },
          "credits_refunded": {
            "type": "integer",
            "description": "Credits refunded to your balance."
          }
        }
      },
      "CancelBySendIdResponse": {
        "type": "object",
        "required": [
          "status",
          "send_id",
          "cancelled_count",
          "credits_refunded"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "send_id": {
            "type": "integer",
            "description": "The send batch ID."
          },
          "cancelled_count": {
            "type": "integer",
            "description": "Number of messages cancelled."
          },
          "credits_refunded": {
            "type": "integer",
            "description": "Total credits refunded."
          }
        }
      },
      "CancelByCustomRefResponse": {
        "type": "object",
        "required": [
          "status",
          "custom_ref",
          "cancelled_count",
          "credits_refunded"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "custom_ref": {
            "type": "string",
            "description": "The custom reference used."
          },
          "cancelled_count": {
            "type": "integer",
            "description": "Number of messages cancelled."
          },
          "credits_refunded": {
            "type": "integer",
            "description": "Total credits refunded."
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "contact_id": {
            "type": "integer",
            "description": "Contact ID."
          },
          "number": {
            "type": "string",
            "description": "Phone number in international format."
          },
          "first_name": {
            "type": "string",
            "description": "First name. Used for the {first_name} placeholder when sending."
          },
          "last_name": {
            "type": "string",
            "description": "Last name. Used for the {last_name} placeholder when sending."
          },
          "company": {
            "type": "string",
            "description": "Company. Used for the {company} placeholder when sending."
          },
          "other": {
            "type": "string",
            "description": "Free-form custom field. Used for the {other} placeholder when sending."
          },
          "field_1": {
            "type": "string",
            "description": "Custom contact field 1. Used for the {field_1} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_2": {
            "type": "string",
            "description": "Custom contact field 2. Used for the {field_2} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_3": {
            "type": "string",
            "description": "Custom contact field 3. Used for the {field_3} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_4": {
            "type": "string",
            "description": "Custom contact field 4. Used for the {field_4} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_5": {
            "type": "string",
            "description": "Custom contact field 5. Used for the {field_5} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          }
        }
      },
      "ListMember": {
        "type": "object",
        "properties": {
          "contact_id": {
            "type": "integer",
            "description": "Contact ID."
          },
          "number": {
            "type": "string",
            "description": "Phone number in international format."
          },
          "first_name": {
            "type": "string",
            "description": "First name. Used for the {first_name} placeholder when sending."
          },
          "last_name": {
            "type": "string",
            "description": "Last name. Used for the {last_name} placeholder when sending."
          },
          "company": {
            "type": "string",
            "description": "Company. Used for the {company} placeholder when sending."
          },
          "other": {
            "type": "string",
            "description": "Free-form custom field. Used for the {other} placeholder when sending."
          },
          "field_1": {
            "type": "string",
            "description": "Custom contact field 1. Used for the {field_1} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_2": {
            "type": "string",
            "description": "Custom contact field 2. Used for the {field_2} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_3": {
            "type": "string",
            "description": "Custom contact field 3. Used for the {field_3} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_4": {
            "type": "string",
            "description": "Custom contact field 4. Used for the {field_4} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_5": {
            "type": "string",
            "description": "Custom contact field 5. Used for the {field_5} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "added": {
            "type": "string",
            "description": "UTC timestamp the contact was added to the list, as \"YYYY-MM-DD HH:MM:SS\"."
          }
        }
      },
      "ContactListResponse": {
        "type": "object",
        "required": [
          "status",
          "total",
          "limit",
          "offset",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "total": {
            "type": "integer",
            "description": "Total contacts matching the filters."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Contact"
            }
          }
        }
      },
      "CreateContactRequest": {
        "type": "object",
        "required": [
          "number"
        ],
        "properties": {
          "number": {
            "type": "string",
            "description": "Phone number in Australian local or international format.",
            "example": "0412345678"
          },
          "first_name": {
            "type": "string",
            "description": "First name. Used for the {first_name} placeholder when sending."
          },
          "last_name": {
            "type": "string",
            "description": "Last name. Used for the {last_name} placeholder when sending."
          },
          "company": {
            "type": "string",
            "description": "Company. Used for the {company} placeholder when sending."
          },
          "other": {
            "type": "string",
            "description": "Free-form custom field. Used for the {other} placeholder when sending."
          },
          "field_1": {
            "type": "string",
            "description": "Custom contact field 1. Used for the {field_1} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_2": {
            "type": "string",
            "description": "Custom contact field 2. Used for the {field_2} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_3": {
            "type": "string",
            "description": "Custom contact field 3. Used for the {field_3} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_4": {
            "type": "string",
            "description": "Custom contact field 4. Used for the {field_4} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_5": {
            "type": "string",
            "description": "Custom contact field 5. Used for the {field_5} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          }
        }
      },
      "UpdateContactRequest": {
        "type": "object",
        "required": [
          "contact_id"
        ],
        "properties": {
          "contact_id": {
            "type": "integer",
            "description": "ID of the contact to update."
          },
          "number": {
            "type": "string",
            "description": "New phone number, in Australian local or international format."
          },
          "first_name": {
            "type": "string",
            "description": "First name. Used for the {first_name} placeholder when sending."
          },
          "last_name": {
            "type": "string",
            "description": "Last name. Used for the {last_name} placeholder when sending."
          },
          "company": {
            "type": "string",
            "description": "Company. Used for the {company} placeholder when sending."
          },
          "other": {
            "type": "string",
            "description": "Free-form custom field. Used for the {other} placeholder when sending."
          },
          "field_1": {
            "type": "string",
            "description": "Custom contact field 1. Used for the {field_1} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_2": {
            "type": "string",
            "description": "Custom contact field 2. Used for the {field_2} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_3": {
            "type": "string",
            "description": "Custom contact field 3. Used for the {field_3} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_4": {
            "type": "string",
            "description": "Custom contact field 4. Used for the {field_4} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          },
          "field_5": {
            "type": "string",
            "description": "Custom contact field 5. Used for the {field_5} placeholder, or for the slug of the field name you configured under Contacts then Manage Fields."
          }
        },
        "description": "Only the fields you supply are changed. At least one updatable field must be present."
      },
      "DeleteContactRequest": {
        "type": "object",
        "required": [
          "contact_id"
        ],
        "properties": {
          "contact_id": {
            "type": "integer",
            "description": "ID of the contact to delete."
          }
        }
      },
      "CreateContactResponse": {
        "type": "object",
        "required": [
          "status",
          "contact_id",
          "number"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "contact_id": {
            "type": "integer",
            "description": "ID of the new contact."
          },
          "number": {
            "type": "string",
            "description": "Normalised phone number."
          }
        }
      },
      "UpdateContactResponse": {
        "type": "object",
        "required": [
          "status",
          "contact_id"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "contact_id": {
            "type": "integer",
            "description": "ID of the updated contact."
          }
        }
      },
      "DeleteContactResponse": {
        "type": "object",
        "required": [
          "status",
          "contact_id",
          "removed"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "contact_id": {
            "type": "integer",
            "description": "ID supplied in the request."
          },
          "removed": {
            "type": "boolean",
            "description": "true when a contact was removed. false when no contact with that ID existed. A missing contact is not a 404."
          }
        }
      },
      "ContactListSummary": {
        "type": "object",
        "properties": {
          "list_id": {
            "type": "integer",
            "description": "List ID."
          },
          "name": {
            "type": "string",
            "description": "List name."
          },
          "created_at": {
            "type": "string",
            "description": "UTC timestamp of creation, as \"YYYY-MM-DD HH:MM:SS\"."
          },
          "contact_count": {
            "type": "integer",
            "description": "Number of contacts in the list."
          }
        }
      },
      "ListsResponse": {
        "type": "object",
        "required": [
          "status",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactListSummary"
            }
          }
        }
      },
      "CreateListRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name for the list.",
            "example": "VIP customers"
          }
        }
      },
      "CreateListResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "name",
          "existing"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer",
            "description": "ID of the list."
          },
          "name": {
            "type": "string",
            "description": "Name of the list."
          },
          "existing": {
            "type": "boolean",
            "description": "true when a list with this name already existed and was returned instead of being created. Creating a duplicate name is not a conflict."
          }
        }
      },
      "RenameListRequest": {
        "type": "object",
        "required": [
          "list_id",
          "name"
        ],
        "properties": {
          "list_id": {
            "type": "integer",
            "description": "ID of the list to rename."
          },
          "name": {
            "type": "string",
            "description": "New name for the list."
          }
        }
      },
      "RenameListResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "name"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "The new name."
          }
        }
      },
      "DeleteListRequest": {
        "type": "object",
        "required": [
          "list_id"
        ],
        "properties": {
          "list_id": {
            "type": "integer",
            "description": "ID of the list to delete."
          },
          "delete_contacts": {
            "type": "boolean",
            "default": false,
            "description": "When true, also permanently delete every contact record that was a member of this list. Any truthy JSON value enables it, including the string \"false\"."
          }
        }
      },
      "DeleteListResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "removed"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer"
          },
          "removed": {
            "type": "boolean",
            "description": "true when the list was removed. A missing list is not a 404."
          }
        }
      },
      "ListMembersResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "total",
          "limit",
          "offset",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer",
            "description": "The list that was queried."
          },
          "total": {
            "type": "integer",
            "description": "Total members of the list."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListMember"
            }
          }
        }
      },
      "ListMemberRequest": {
        "type": "object",
        "required": [
          "list_id"
        ],
        "description": "Identify the contact with either contact_id or number. If contact_id is supplied and greater than zero, number is ignored.",
        "properties": {
          "list_id": {
            "type": "integer",
            "description": "ID of the list."
          },
          "contact_id": {
            "type": "integer",
            "description": "ID of the contact."
          },
          "number": {
            "type": "string",
            "description": "Australian mobile number of the contact."
          }
        }
      },
      "AddListMemberResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "contact_id",
          "added"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer"
          },
          "contact_id": {
            "type": "integer"
          },
          "added": {
            "type": "boolean",
            "description": "true when the contact was added. false when it was already a member. An existing membership is not a conflict."
          }
        }
      },
      "RemoveListMemberResponse": {
        "type": "object",
        "required": [
          "status",
          "list_id",
          "contact_id",
          "removed"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "list_id": {
            "type": "integer"
          },
          "contact_id": {
            "type": "integer"
          },
          "removed": {
            "type": "boolean",
            "description": "true when the membership was removed."
          }
        }
      },
      "Unsubscribe": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Phone number in international format."
          },
          "updated_at": {
            "type": "string",
            "description": "UTC timestamp of the unsubscribe, as \"YYYY-MM-DD HH:MM:SS\"."
          }
        }
      },
      "UnsubscribesResponse": {
        "type": "object",
        "required": [
          "status",
          "total",
          "limit",
          "offset",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unsubscribe"
            }
          }
        }
      },
      "UnsubscribeRequest": {
        "type": "object",
        "required": [
          "number"
        ],
        "properties": {
          "number": {
            "type": "string",
            "description": "Phone number, in Australian local or international format.",
            "example": "0412345678"
          }
        }
      },
      "AddUnsubscribeResponse": {
        "type": "object",
        "required": [
          "status",
          "number",
          "added"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "number": {
            "type": "string",
            "description": "Normalised phone number."
          },
          "added": {
            "type": "boolean",
            "description": "true when the number was added. false when it was already unsubscribed."
          }
        }
      },
      "RemoveUnsubscribeResponse": {
        "type": "object",
        "required": [
          "status",
          "number",
          "removed"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "number": {
            "type": "string",
            "description": "Normalised phone number."
          },
          "removed": {
            "type": "boolean",
            "description": "true when the number was removed from your unsubscribe list."
          }
        }
      },
      "AccountBalanceResponse": {
        "type": "object",
        "required": [
          "status",
          "credit_balance"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "credit_balance": {
            "type": "integer",
            "description": "Your current SMS credit balance.",
            "example": 9981
          }
        }
      },
      "Sender": {
        "type": "object",
        "properties": {
          "sender": {
            "type": "string",
            "description": "Sender ID value, either an alphanumeric name or a phone number."
          },
          "type": {
            "type": "string",
            "description": "Sender type. Observed values are alpha, own, dedicated and shared.",
            "example": "alpha"
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Label for this sender in your account."
          },
          "is_default": {
            "type": "boolean",
            "description": "Whether this is your default sender."
          }
        }
      },
      "SendersResponse": {
        "type": "object",
        "required": [
          "status",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sender"
            },
            "description": "Active Sender IDs only."
          }
        }
      },
      "RegisterSenderRequest": {
        "type": "object",
        "required": [
          "number"
        ],
        "properties": {
          "number": {
            "type": "string",
            "description": "The mobile number to register as a sender.",
            "example": "0412345678"
          },
          "label": {
            "type": "string",
            "description": "A label to identify this sender in your account."
          }
        }
      },
      "RegisterSenderResponse": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "verification_sent"
            ],
            "description": "Note that this endpoint returns \"verification_sent\" rather than \"complete\"."
          },
          "message": {
            "type": "string",
            "example": "A verification link has been sent to the number."
          }
        }
      },
      "AcmaRegistration": {
        "type": "object",
        "properties": {
          "sender_id": {
            "type": "integer",
            "description": "Internal sender ID. Pass it back as sender_id in a POST to re-register a previously rejected sender."
          },
          "sender": {
            "type": "string",
            "description": "The alphanumeric Sender ID text."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pending"
            ],
            "description": "Overall sender status. \"active\" means approved and usable for sending, \"pending\" means registration is still in progress."
          },
          "acma_status": {
            "type": "string",
            "nullable": true,
            "description": "Current stage of the ACMA registration, or null when the sender was created before ACMA registration was required. Documented values are pending_internal_review, pending_email_verification, pending_acma_submission, pending_acma, submitted_wait, verified, rejected_internal and acma_error. The value pending_reuse_approval also occurs."
          },
          "brand_id": {
            "type": "integer",
            "nullable": true,
            "description": "ID of the associated brand, matching an entry in brands."
          },
          "partner_id": {
            "type": "integer",
            "nullable": true,
            "description": "ID of the associated on-behalf-of partner, matching an entry in partners, or null."
          }
        }
      },
      "AcmaBrand": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Brand ID. Pass it as brand_id with brand_mode set to \"existing\"."
          },
          "brand_name": {
            "type": "string",
            "description": "Organisation name."
          },
          "abn": {
            "type": "string",
            "description": "Australian Business Number."
          },
          "website": {
            "type": "string",
            "description": "Website URL."
          }
        }
      },
      "AcmaPartner": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Partner ID. Pass it as partner_id when registering on behalf of a previously used client."
          },
          "partner_name": {
            "type": "string",
            "description": "Organisation name."
          },
          "abn": {
            "type": "string",
            "description": "Australian Business Number."
          },
          "website": {
            "type": "string",
            "description": "Website URL."
          }
        }
      },
      "AcmaListingResponse": {
        "type": "object",
        "required": [
          "registrations",
          "brands",
          "partners"
        ],
        "properties": {
          "registrations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcmaRegistration"
            },
            "description": "Every alphanumeric Sender ID on your account."
          },
          "brands": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcmaBrand"
            },
            "description": "Saved organisations."
          },
          "partners": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AcmaPartner"
            },
            "description": "Saved on-behalf-of organisations."
          }
        }
      },
      "AcmaSenderStatusResponse": {
        "type": "object",
        "required": [
          "sender",
          "sender_id"
        ],
        "properties": {
          "sender": {
            "type": "string",
            "description": "The Sender ID text."
          },
          "sender_id": {
            "type": "integer",
            "description": "Internal sender ID."
          },
          "acma_status": {
            "type": "string",
            "nullable": true,
            "description": "Current stage of the ACMA registration, or null when the sender was created before ACMA registration was required. Documented values are pending_internal_review, pending_email_verification, pending_acma_submission, pending_acma, submitted_wait, verified, rejected_internal and acma_error. The value pending_reuse_approval also occurs."
          },
          "brand_id": {
            "type": "integer",
            "nullable": true,
            "description": "Associated brand ID."
          },
          "chatbot_id": {
            "type": "integer",
            "nullable": true,
            "description": "Associated ACMA registration record ID."
          },
          "partner_id": {
            "type": "integer",
            "nullable": true,
            "description": "Associated partner ID."
          }
        }
      },
      "AcmaRegistrationRequest": {
        "type": "object",
        "required": [
          "sender",
          "sender_id_relation_type",
          "contact_first_name",
          "contact_last_name",
          "contact_email"
        ],
        "description": "Brand fields describe the organisation the Sender ID belongs to. When registering on behalf of a client, the brand fields hold the client's details and the applicant fields hold yours.",
        "properties": {
          "sender": {
            "type": "string",
            "minLength": 1,
            "maxLength": 11,
            "description": "The alphanumeric Sender ID to register. 1 to 11 printable ASCII characters, and it cannot be only digits.",
            "example": "MyBrand"
          },
          "sender_id": {
            "type": "integer",
            "minimum": 1,
            "description": "An existing sender_id on your account to re-register, for example after a rejection. If omitted a new sender record is created. When supplied, the stored Sender ID text wins over the sender field."
          },
          "sender_id_relation_type": {
            "type": "string",
            "enum": [
              "registered_company_name",
              "registered_business_name",
              "registered_trade_mark",
              "registered_domain_name"
            ],
            "description": "How the Sender ID relates to the organisation."
          },
          "sender_id_relation_detail": {
            "type": "string",
            "description": "Required unless sender_id_relation_type is registered_company_name. For registered_business_name, the registered business name as it appears on the ABR for the ABN supplied, and the Sender ID must contain that name or a recognisable abbreviation. For registered_trade_mark, the trademark name exactly as registered with IP Australia. For registered_domain_name, the .au domain name whose registrant matches the ABN supplied."
          },
          "contact_first_name": {
            "type": "string",
            "description": "Contact first name at the brand organisation."
          },
          "contact_last_name": {
            "type": "string",
            "description": "Contact last name at the brand organisation."
          },
          "contact_email": {
            "type": "string",
            "description": "Contact email address at the brand organisation. Not format checked."
          },
          "brand_mode": {
            "type": "string",
            "enum": [
              "new",
              "existing"
            ],
            "default": "new",
            "description": "Set to \"existing\" to reuse a saved brand by its brand_id. Any value other than the exact string \"existing\" is treated as \"new\"."
          },
          "brand_id": {
            "type": "integer",
            "description": "Required when brand_mode is \"existing\". A brand ID from GET /v1/acma-registration."
          },
          "abn": {
            "type": "string",
            "description": "Australian Business Number. Must contain exactly 11 digits once non-digits are stripped. Required when brand_mode is \"new\"."
          },
          "business_name": {
            "type": "string",
            "description": "Registered business name. Required when brand_mode is \"new\"."
          },
          "address_line1": {
            "type": "string",
            "description": "Business street address. Required when brand_mode is \"new\"."
          },
          "address_line2": {
            "type": "string",
            "description": "Additional address line."
          },
          "suburb": {
            "type": "string",
            "description": "Suburb or city. Required when brand_mode is \"new\"."
          },
          "state": {
            "type": "string",
            "enum": [
              "ACT",
              "NSW",
              "NT",
              "QLD",
              "SA",
              "TAS",
              "VIC",
              "WA"
            ],
            "description": "Australian state or territory, case insensitive. Required when brand_mode is \"new\"."
          },
          "postcode": {
            "type": "string",
            "description": "Postcode. Required when brand_mode is \"new\". Not format checked."
          },
          "website": {
            "type": "string",
            "description": "Business website URL. Required when brand_mode is \"new\". Ignored when brand_mode is \"existing\", where the saved brand website is used."
          },
          "business_phone": {
            "type": "string",
            "description": "Business phone number. Required when brand_mode is \"new\". Not format checked."
          },
          "on_behalf": {
            "type": "boolean",
            "default": false,
            "description": "Set to true when registering for a business that is not your own."
          },
          "partner_id": {
            "type": "integer",
            "description": "Reuse a saved partner by ID from GET /v1/acma-registration. When supplied, the applicant fields are not required."
          },
          "applicant_abn": {
            "type": "string",
            "description": "Applicant ABN. Must contain exactly 11 digits. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_name": {
            "type": "string",
            "description": "Applicant organisation name. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_contact_first_name": {
            "type": "string",
            "description": "Applicant contact first name. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_contact_last_name": {
            "type": "string",
            "description": "Applicant contact last name. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_contact_email": {
            "type": "string",
            "description": "Applicant contact email. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_website": {
            "type": "string",
            "description": "Applicant website URL. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_phone": {
            "type": "string",
            "description": "Applicant business phone number. Always optional."
          },
          "applicant_address_line1": {
            "type": "string",
            "description": "Applicant street address. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_address_line2": {
            "type": "string",
            "description": "Additional address line."
          },
          "applicant_suburb": {
            "type": "string",
            "description": "Applicant suburb or city. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_state": {
            "type": "string",
            "enum": [
              "ACT",
              "NSW",
              "NT",
              "QLD",
              "SA",
              "TAS",
              "VIC",
              "WA"
            ],
            "description": "Applicant state or territory. Required when on_behalf is true and no partner_id is supplied."
          },
          "applicant_postcode": {
            "type": "string",
            "description": "Applicant postcode. Required when on_behalf is true and no partner_id is supplied."
          }
        }
      },
      "AcmaRegistrationResponse": {
        "type": "object",
        "required": [
          "status",
          "message",
          "sender_id",
          "brand_id",
          "chatbot_id",
          "partner_id",
          "acma_status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "message": {
            "type": "string",
            "example": "Sender ID registration submitted and is pending review."
          },
          "sender_id": {
            "type": "integer",
            "description": "Internal sender ID, newly created if it did not already exist."
          },
          "brand_id": {
            "type": "integer",
            "description": "Brand ID used or created. An existing brand with the same ABN is reused."
          },
          "chatbot_id": {
            "type": "integer",
            "description": "ACMA registration record ID."
          },
          "partner_id": {
            "type": "integer",
            "nullable": true,
            "description": "Partner ID when registering on behalf of a client, otherwise null."
          },
          "acma_status": {
            "type": "string",
            "enum": [
              "pending_internal_review"
            ],
            "description": "Always \"pending_internal_review\" on a successful submission."
          }
        }
      },
      "InboundMessage": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the inbound message."
          },
          "from": {
            "type": "string",
            "description": "The number the message came from."
          },
          "to": {
            "type": "string",
            "description": "Your dedicated number that received the message."
          },
          "message": {
            "type": "string",
            "description": "Message content."
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "unsubscribe"
            ],
            "description": "\"inbound\" for a normal reply, \"unsubscribe\" for an opt-out."
          },
          "received_at": {
            "type": "string",
            "description": "UTC timestamp when received, as \"YYYY-MM-DD HH:MM:SS\"."
          }
        }
      },
      "InboundResponse": {
        "type": "object",
        "required": [
          "status",
          "total",
          "limit",
          "offset",
          "results"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboundMessage"
            }
          }
        }
      },
      "WebhooksResponse": {
        "type": "object",
        "required": [
          "status",
          "webhooks",
          "has_signing_secret"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "webhooks": {
            "type": "object",
            "description": "Your configured webhook URLs.",
            "properties": {
              "inbound": {
                "type": "string",
                "nullable": true,
                "description": "URL that receives inbound message notifications, or null."
              },
              "status": {
                "type": "string",
                "nullable": true,
                "description": "URL that receives delivery receipts, or null."
              }
            }
          },
          "has_signing_secret": {
            "type": "boolean",
            "description": "true when a webhook signing secret is set on the account, which means every webhook is signed with X-MM-Timestamp and X-MM-Signature."
          }
        }
      },
      "SetWebhookRequest": {
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "status"
            ],
            "description": "Which webhook to set."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 1024,
            "description": "The URL that will receive POST notifications. Maximum 1024 characters. Hosts that resolve to private or internal addresses are rejected.",
            "example": "https://example.com/hooks/mobilemessage"
          }
        }
      },
      "DeleteWebhookRequest": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "status"
            ],
            "description": "Which webhook to remove."
          }
        }
      },
      "SetWebhookResponse": {
        "type": "object",
        "required": [
          "status",
          "message",
          "type",
          "url"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "message": {
            "type": "string",
            "example": "Webhook subscribed successfully"
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "status"
            ],
            "description": "The webhook type that was set."
          },
          "url": {
            "type": "string",
            "description": "The URL that was saved."
          }
        }
      },
      "DeleteWebhookResponse": {
        "type": "object",
        "required": [
          "status",
          "message",
          "type"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "complete"
            ]
          },
          "message": {
            "type": "string",
            "example": "Webhook unsubscribed successfully"
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "status"
            ],
            "description": "The webhook type that was removed."
          }
        }
      },
      "WebhookInbound": {
        "type": "object",
        "description": "Payload POSTed to your inbound webhook URL when a reply or opt-out arrives on one of your numbers. See the Webhooks section of https://mobilemessage.com.au/api-documentation for the full description.\n\nDelivered as an HTTP POST with a 5 second connection timeout and a 5 second response timeout. Any 2xx marks the webhook delivered, anything else schedules a retry. Up to 10 attempts are made per event, backing off 1, 2, 4, 8, 16 and 32 minutes and then capping at 60 minutes, roughly 4 hours in total before the event is marked failed. Webhooks are not guaranteed to arrive in order, so use the payload fields rather than arrival order, and make your handler idempotent. When a webhook signing secret is set on the account, every attempt also carries X-MM-Timestamp and X-MM-Signature, regenerated for each attempt.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Your number that received the message."
          },
          "message": {
            "type": "string",
            "description": "Content of the SMS."
          },
          "sender": {
            "type": "string",
            "description": "The number the message came from."
          },
          "received_at": {
            "type": "string",
            "description": "UTC timestamp when the event was received."
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "unsubscribe"
            ],
            "description": "\"inbound\" for a reply, \"unsubscribe\" for an opt-out."
          },
          "original_message_id": {
            "type": "string",
            "description": "UUID of the outbound message this reply is matched to, which is the same value returned as results[].message_id when you sent it. Matching uses the two phone numbers and picks the most recent message sent to that number from that sender. Empty string when no outbound message matched."
          },
          "original_custom_ref": {
            "type": "string",
            "description": "Your custom reference for the original outbound message. Empty string when none was supplied."
          }
        }
      },
      "WebhookDeliveryReceipt": {
        "type": "object",
        "description": "Payload POSTed to your status webhook URL when a delivery receipt arrives. A message longer than 160 GSM-7 characters, or 70 Unicode characters, is sent as several parts and produces one delivery receipt per part, so key your handler on message_id plus part_number. See the Webhooks section of https://mobilemessage.com.au/api-documentation for the full description.\n\nDelivered as an HTTP POST with a 5 second connection timeout and a 5 second response timeout. Any 2xx marks the webhook delivered, anything else schedules a retry. Up to 10 attempts are made per event, backing off 1, 2, 4, 8, 16 and 32 minutes and then capping at 60 minutes, roughly 4 hours in total before the event is marked failed. Webhooks are not guaranteed to arrive in order, so use the payload fields rather than arrival order, and make your handler idempotent. When a webhook signing secret is set on the account, every attempt also carries X-MM-Timestamp and X-MM-Signature, regenerated for each attempt.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient phone number."
          },
          "message": {
            "type": "string",
            "description": "Content of the SMS."
          },
          "sender": {
            "type": "string",
            "description": "Sender ID the message was sent from."
          },
          "custom_ref": {
            "type": "string",
            "nullable": true,
            "description": "Your custom reference for the outbound message."
          },
          "status": {
            "type": "string",
            "enum": [
              "delivered",
              "failed"
            ],
            "description": "Final delivery outcome for this part."
          },
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the outbound message."
          },
          "received_at": {
            "type": "string",
            "description": "UTC timestamp when the receipt was received."
          },
          "part_number": {
            "type": "integer",
            "description": "Part number this receipt covers. 1 for a non-concatenated message."
          },
          "total_parts": {
            "type": "integer",
            "description": "Total parts for the outbound message. 1 for a non-concatenated message."
          }
        }
      },
      "TestInboundRequest": {
        "type": "object",
        "required": [
          "from",
          "message"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "The mobile number the reply comes from, in Australian local or international format.",
            "example": "0412345678"
          },
          "message": {
            "type": "string",
            "maxLength": 1600,
            "description": "The reply content. Start it with STOP to simulate an opt-out.",
            "example": "Thanks, see you then!"
          },
          "to": {
            "type": "string",
            "description": "Which of the account's sender IDs received the reply. Optional; defaults to the sender of the last message sent to that number.",
            "example": "YourBrand"
          }
        }
      },
      "TestInboundResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "unsubscribe"
            ],
            "description": "unsubscribe when the message was treated as an opt-out.",
            "example": "inbound"
          },
          "to": {
            "type": "string",
            "example": "YourBrand"
          },
          "from": {
            "type": "string",
            "example": "61412345678"
          },
          "webhook_queued": {
            "type": "boolean",
            "description": "True when the account has an inbound webhook URL and a webhook was queued.",
            "example": true
          }
        }
      }
    }
  }
}
