====== voip.ms SMS/MMS REST API ======

Reference for the voip.ms SMS/MMS REST API. Sanitized — no account credentials or phone numbers. Account-specific values (API username/password, DID numbers) are supplied at runtime via environment variables and kept local.

===== Endpoint & authentication =====

  * **Base URL:** ''https://voip.ms/api/v1/rest.php'' (use ''voip.ms'', **not** ''www.voip.ms'' — the ''www'' host 302-redirects and drops POST bodies, producing ''missing_method'').
  * **Auth (every request):** ''api_username'', ''api_password'', ''method''.
    - ''api_username'' is the **portal login email** — //not// the 6-digit account ID.
    - ''api_password'' is set under **Main Menu → SOAP / REST API → API Security** and is distinct from the portal login password.
    - The calling server's public IP must be **whitelisted** on the same API Security page.
  * **Transport:** GET with query params for SMS; POST ''multipart/form-data'' for MMS with base64 images.
  * A separate **bearer token / API key** exists for the 3CX webhook flow — it is **not** used by ''rest.php''.

===== Limits & pricing =====

  * **API sending cap: 100 SMS/MMS per day** (portal is unlimited). Raise via support ticket.
  * **SMS:** $0.0075 each way. **MMS:** $0.02 each way.
  * US/Canada 10-digit DIDs only; short codes and 2FA codes aren't guaranteed; public URL shorteners (bit.ly etc.) may be blocked.
  * MMS attachments: types ''JPG, GIF, JPEG, PNG, MP3, WAV, MIDI, MP4, 3GP''; ≤1300 KB each; up to 3 files; text ≤2048 chars.

===== Methods =====

==== sendSMS — send a text ====

^ Param ^ Req ^ Notes ^
| did | yes | Sender DID, e.g. ''5551234567'' |
| dst | yes | Destination number |
| message | yes | Max **160** chars |

Returns ''{"status":"success","sms":<new id>}''. Requires SMS enabled on the DID (see ''setSMS'' below), else ''{"status":"sms_failed","message":"The SMS message was not sent"}''.

==== sendMMS — send media/text ====

^ Param ^ Req ^ Notes ^
| did | yes | Sender DID |
| dst | yes | Destination |
| message | yes | Max **2048** chars |
| media1 | no | URL to a media file |
| media2 | no | Base64 image (''data:image/png;base64,...'') |
| media3 | no | Reserved/empty |

  * POST + base64 → up to **1.2 MB/file**; GET + base64 → ~160 KB (~512-char URL limit). File-URL submissions have no size limit either way.

==== getSMS — list/retrieve texts ====

^ Param ^ Req ^ Notes ^
| sms | no | ID for a specific SMS |
| from | no | Start date ''YYYY-MM-DD'' (default: today) |
| to | no | End date (default: today) |
| type | no | ''1''=received, ''0''=sent |
| did | no | Filter by DID |
| contact | no | Filter by contact number |
| limit | no | Records to return (default **50**) |
| timezone | no | Numeric -12 to 13 |
| all_messages | no | ''1'' = SMS + MMS combined, ''0'' = SMS only (id must be ''0'' when set) |

Returns ''{"status":"success","sms":[ ...rows... ]}''. **Empty result returns ''{"status":"no_sms","message":"There are no SMS messages"}''** — treat as ''[]'', not an error.

==== getMMS — list/retrieve media messages ====

Same shape as ''getSMS'' but the id param is ''mms''. ''all_messages=1'' returns MMS+SMS combined (id must be ''0''). Empty result also reports ''status: no_sms''.

==== getMediaMMS — fetch attachments for a message ====

^ Param ^ Req ^ Notes ^
| id | yes | MMS id |
| media_as_array | no | ''1''=array, ''0''=JSON object (default) |

Returns ''{"status":"success","id":...,"date":...,"media":[url,...]}'' (up to 3 slots).

==== deleteSMS / deleteMMS — delete one message ====

^ Param ^ Req ^ Notes ^
| id | yes | Message id |

Returns ''{"status":"success"}''.

==== setSMS — enable/disable SMS on a DID ====

^ Param ^ Req ^ Notes ^
| did | yes | DID to update |
| enable | yes | ''1'' to enable, ''0'' to disable |
| email_enabled | no | bool — forward inbound SMS to email |
| email_address | no | email address(es), comma-separated |
| sms_forward_enable | no | bool — forward inbound SMS to another number |
| sms_forward | no | forwarding number |
| url_callback_enable | no | bool — GET callback on inbound |
| url_callback | no | callback URL (variables below) |
| url_callback_retry | no | bool — require ''ok'' response, retry every 30 min |

SMS must be enabled on a DID (''enable=1'') before ''sendSMS'' works.

==== getDIDsInfo — list DIDs on the account ====

Returns ''{"status":"success","dids":[ ... ]}''. Each DID includes ''sms_available'', ''sms_enabled'', ''mms_available'', and the full SMS config fields — use this to find a SMS-capable DID and check whether SMS is enabled.

Note: ''getDIDs'', ''getDIDInfo'', ''getDID'' are **not** valid methods — use ''getDIDsInfo''.

===== Inbound via URL Callback (per-DID) =====

Configure on the DID (portal Manage DID, or ''setSMS''):

<code>
https://your.host/sms?to={TO}&from={FROM}&message={MESSAGE}&id={ID}&date={TIMESTAMP}&media={MEDIA}
</code>

Variables: ''{ID}'', ''{TIMESTAMP}'', ''{FROM}'', ''{TO}'', ''{MESSAGE}'', ''{MEDIA}'' (comma-separated media list, for MMS).

**Retry:** if "URL Callback Retry" is enabled, your endpoint must respond with the literal body ''ok''. Without it, voip.ms re-sends the same message **every 30 minutes**.

===== Dialing mode (per-DID, for 3CX/API) =====

Choose NANPA (10-digit, no country code) or E164 (''+1'' + 10 digits). Affects what you send to the API and what arrives in callbacks — normalize on your side.

===== Gotchas =====

  * ''api_username'' is the portal **email**, not the account ID.
  * Empty ''getSMS''/''getMMS'' results report ''status: no_sms'' (even for MMS) — return ''[]''.
  * SMS must be enabled per-DID (''setSMS enable=1'') before sending, else ''sms_failed''.
  * ''sendSMS''/''sendMMS'' reuse the ''sms'' response key for the new message id (int); ''getSMS'' uses ''sms'' for the row list; ''getMMS'' uses ''mms''.
  * No ''markSMSRead'' method exists.
  * SIP/SMS over SIP MESSAGE is text-only (no MMS) and uses a separate SIP sub-account, not the REST API.
  * DID **POP must match** the registered SIP server or inbound calls won't ring.
  * Transient transport errors occur; clients should retry with backoff.

===== Other ingestion channels (non-REST) =====

  * **Email to SMS:** mail ''sms@voip.ms'', subject = recipient 10-digit number; security-code / from-DID overrides via dot syntax ''dst.seccode.fromdid''.
  * **SMPP:** ''smpp.voip.ms:2775'' (plain) / '':3550'' (TLS, scheme ''ssmpp''); bind as transceiver; ''source_addr'' = SMS-enabled DID. Bypasses the 100/day REST cap.
  * **SIP MESSAGE (RFC 3428):** SMS only, user/pass-auth trunks; original destination in ''X-Sms-To'' header on receive.

----

//Source: voip.ms official API docs (''/m/apidocs.php'') + SMS/MMS wiki article, verified live.//