> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Messaging Services API

> Messaging Services endpoints exposed by the Devotel CPaaS API

# Messaging Services API

Messaging Services endpoints exposed by the Devotel CPaaS API

**Base path:** `/api/v1/messaging`

**Endpoint count:** 14

***

### Get messaging data residency

<Note>
  `GET /api/v1/messaging/data-residency`
</Note>

Read the messaging data plane's residency posture for the tenant: the pinned storage region and whether it is enforced (projected from the org's compliance data-residency pin), plus the API region this request was processed in and the region-pinned ingress endpoint. Use it to confirm — programmatically, next to sender pools and services — where SMS / MMS / chat bodies and metadata rest and are processed before onboarding a regulated buyer. Read-only; pin or enforce a region via `PUT /v1/compliance/data-residency`.

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/data-residency" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/data-residency', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/data-residency", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/data-residency", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/data-residency')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/data-residency');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### List messaging services

<Note>
  `GET /api/v1/messaging/services`
</Note>

List every messaging service configured in the tenant, newest-first. A messaging service is a Twilio-MessagingServiceSid-style container that bundles a sender pool, opt-out list, inbound webhook, throughput cap and sending flags under one id you reference from `/v1/messages`. Use it to populate a service picker or reconcile your configuration. Returns up to 500 services; an empty array means none are configured yet.

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Get messaging service

<Note>
  `GET /api/v1/messaging/services/{id}`
</Note>

Fetch a single messaging service by its Orbit id, returning its full configuration — sender pool, per-country pool overrides, opt-out list, inbound/fallback/status-callback URLs, sending flags, validity period, throughput cap, external id and feature flags. Use it to hydrate a detail/edit view. Returns 404 when the id is not a messaging service in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/{id}" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/{id}", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### List messaging service phone numbers

<Note>
  `GET /api/v1/messaging/services/{id}/phone-numbers`
</Note>

List every tenant-owned phone number (DID) attached to a messaging service, joined with each number's E.164, country code and capabilities for display, newest-attached first. Use it to render the service's sender-number table. Returns 404 when the id is not a messaging service in this tenant; an empty array means no numbers are attached yet.

<ParamField path="id" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Get messaging service throughput cap

<Note>
  `GET /api/v1/messaging/services/{id}/throughput`
</Note>

Read the per-service outbound throughput cap (messages per second) plus the min/max bounds the dashboard renders the control against. `mps_cap` is null when the service is uncapped (inherits only the tenant/provider limits). Use it to hydrate the throughput slider before an operator raises or clears the cap. Returns 404 when the id is not a messaging service in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Get messaging service throughput stats

<Note>
  `GET /api/v1/messaging/services/{id}/throughput-stats`
</Note>

Read live throughput telemetry for one messaging service, derived from the Redis token bucket the send path enforces: current send rate, token-bucket utilization against the configured cap, over-cap queue depth and recently observed throttle (429) events, joined with the service label. Use it to drive a per-service throughput gauge or alert on sustained throttling. Returns 404 when the id is not a messaging service in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput-stats');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Look up messaging service by external id

<Note>
  `GET /api/v1/messaging/services/lookup`
</Note>

Resolve a messaging service by its external id — a Twilio MessagingServiceSid (`MGxxx`) or any opaque vendor id you stored on the service — and return the matching Orbit resource with its full configuration. Pass the id as the `external_id` query parameter. Used by the Twilio migrator import flow and the dashboard's "I came from Twilio" wizard to map a foreign id onto the Orbit resource. Returns 404 when no service in this tenant carries that external id, so the caller can branch into a create-new flow without parsing a 200 body.

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/lookup" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/lookup', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/lookup", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/lookup", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/lookup')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/lookup');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Get account messaging throughput stats

<Note>
  `GET /api/v1/messaging/services/throughput-stats`
</Note>

Read a tenant-wide throughput rollup across every messaging service, derived from the Redis token buckets the send path enforces. Reads each service's live sample concurrently and sums the totals — current send rate, configured cap, over-cap queue depth and recently observed throttle (429) events — alongside a per-service breakdown and the count of capped services. Use it to drive an account-level throughput dashboard or alert on sustained throttling. Returns an empty `services` array when no messaging services are configured yet.

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X GET "https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats" \
      -H "X-API-Key: dv_live_sk_your_key_here" 
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats', {
      method: 'GET',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
      },
    })
    console.log(await res.json())


    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    r = requests.get("https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats", headers=headers)
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats", nil)
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))

    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats')
    req = Net::HTTP::Get.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']


    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/throughput-stats');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),

    ]);

    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Create messaging service

<Note>
  `POST /api/v1/messaging/services`
</Note>

Create a messaging service — the container `/v1/messages` resolves once at send time to copy sender pool, opt-out list, inbound webhook, sending flags and throughput cap onto the message. Only `label` is required; attach a `sender_pool_id` (or per-country `country_sender_pools`), an `opt_out_list_id`, inbound/fallback/status-callback URLs, an `mps_cap`, and an `external_id` (Twilio MGxxx) so the migrator can reconcile an imported service. Use it when standing up a new sending profile. Owner / admin only; a duplicate `external_id` returns 409.

<ParamField body="label" type="string">
  Human-readable name (1–200 chars, required).
</ParamField>

<ParamField body="sender_pool_id" type="string">
  Default sender pool the send path resolves `from` through.
</ParamField>

<ParamField body="opt_out_list_id" type="string">
  —
</ParamField>

<ParamField body="inbound_webhook_url" type="string">
  —
</ParamField>

<ParamField body="inbound_method" type="string (enum: POST|GET)">
  —
</ParamField>

<ParamField body="mps_cap" type="integer">
  Optional throughput cap (messages/sec).
</ParamField>

<ParamField body="external_id" type="string">
  Twilio MGxxx / opaque vendor id for migrator import.
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://api.orbit.devotel.io/api/v1/messaging/services" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.post("https://api.orbit.devotel.io/api/v1/messaging/services", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/messaging/services", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services')
    req = Net::HTTP::Post.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Attach phone number to messaging service

<Note>
  `POST /api/v1/messaging/services/{id}/phone-numbers`
</Note>

Attach a tenant-owned phone number (DID) to a messaging service so it becomes a candidate sender for outbound SMS through that service. The number must belong to this tenant and be SMS-capable (a voice/fax/MMS-only DID is rejected with 422). Re-attaching the same number is idempotent — it returns the existing binding rather than erroring. Body: `{ phone_number_id }`. Owner / admin only; returns 404 when the service or number is not found in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<ParamField body="phone_number_id" type="string">
  Id of a tenant-owned, SMS-capable DID to attach (required).
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.post("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers')
    req = Net::HTTP::Post.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Update messaging service

<Note>
  `PATCH /api/v1/messaging/services/{id}`
</Note>

Update mutable fields on a messaging service in place — send only the fields you want to change (at least one is required). Nullable fields (`sender_pool_id`, `opt_out_list_id`, the webhook URLs, `validity_period_seconds`, `mps_cap`, `external_id`) accept `null` to clear them; pass `country_sender_pools: {}` to remove all per-country overrides. Changes bust the per-service send cache so the next send reads them within seconds. Owner / admin only; returns 404 when the id is not a messaging service in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<ParamField body="label" type="string">
  —
</ParamField>

<ParamField body="sender_pool_id" type="string | null">
  —
</ParamField>

<ParamField body="opt_out_list_id" type="string | null">
  —
</ParamField>

<ParamField body="mps_cap" type="integer | null">
  —
</ParamField>

<ParamField body="sticky_sender" type="boolean">
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X PATCH "https://api.orbit.devotel.io/api/v1/messaging/services/{id}" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}', {
      method: 'PATCH',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.patch("https://api.orbit.devotel.io/api/v1/messaging/services/{id}", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("PATCH", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}')
    req = Net::HTTP::Patch.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Update messaging service throughput cap

<Note>
  `PATCH /api/v1/messaging/services/{id}/throughput`
</Note>

Set or clear the per-service outbound throughput cap (messages per second, 1–1000). Send a numeric `mps_cap` to throttle sends that reference this service through the shared token-bucket limiter; send `null` to remove the cap and inherit only the tenant/provider limits. Use it to protect downstream carriers or a bursty campaign from starving the tenant pool. Owner / admin only; returns 404 when the id is not a messaging service in this tenant.

<ParamField path="id" type="string" required>
  —
</ParamField>

<ParamField body="mps_cap" type="integer | null">
  Messages-per-second cap (1–1000), or null to clear the cap.
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X PATCH "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput', {
      method: 'PATCH',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.patch("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("PATCH", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput')
    req = Net::HTTP::Patch.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/throughput');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Delete messaging service

<Note>
  `DELETE /api/v1/messaging/services/{id}`
</Note>

Permanently delete a messaging service and clean up its phone-number bindings. New `/v1/messages` calls referencing the id fail to resolve immediately; messages already sent are unaffected. Use it to retire a sending profile you no longer need. Owner / admin only; returns 404 when the id is not a messaging service in this tenant and 204 No Content on success.

<ParamField path="id" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X DELETE "https://api.orbit.devotel.io/api/v1/messaging/services/{id}" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}', {
      method: 'DELETE',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.delete("https://api.orbit.devotel.io/api/v1/messaging/services/{id}", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("DELETE", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}')
    req = Net::HTTP::Delete.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***

### Detach phone number from messaging service

<Note>
  `DELETE /api/v1/messaging/services/{id}/phone-numbers/{numberId}`
</Note>

Remove a phone number (DID) binding from a messaging service so it is no longer a candidate sender for that service. The DID itself is not released — only the association is dropped. Use it to prune a number from a sending profile. Owner / admin only; returns 404 when the binding (service or number) does not exist in this tenant and 204 No Content on success.

<ParamField path="id" type="string" required>
  —
</ParamField>

<ParamField path="numberId" type="string" required>
  —
</ParamField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X DELETE "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from '@devotel-orbit/node'

    const orbit = new Orbit({
      apiKey: process.env.ORBIT_API_KEY!,
    })

    const res = await fetch('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}', {
      method: 'DELETE',
      headers: {
        'X-API-Key': process.env.ORBIT_API_KEY!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({}),
    })
    console.log(await res.json())
    ```

    ```python Python theme={null}
    import os, requests

    headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
    headers["Content-Type"] = "application/json"
    r = requests.delete("https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}", headers=headers, json={})
    print(r.json())
    ```

    ```go Go theme={null}
    package main

    import (
    	"bytes"
    	"net/http"
    	"os"
    )

    func main() {
    	req, _ := http.NewRequest("DELETE", "https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}", bytes.NewBuffer([]byte(`{}`)))
    	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
    	req.Header.Set("Content-Type", "application/json")
    	http.DefaultClient.Do(req)
    }
    ```

    ```ruby Ruby theme={null}
    require 'net/http'
    require 'json'

    uri = URI('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}')
    req = Net::HTTP::Delete.new(uri)
    req['X-API-Key'] = ENV['ORBIT_API_KEY']
    req['Content-Type'] = 'application/json'
    req.body = {}.to_json
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    puts res.body
    ```

    ```php PHP theme={null}
    <?php
    $ch = curl_init('https://api.orbit.devotel.io/api/v1/messaging/services/{id}/phone-numbers/{numberId}');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'X-API-Key: ' . getenv('ORBIT_API_KEY'),
      'Content-Type: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
    {}
    JSON);
    echo curl_exec($ch);
    ```
  </CodeGroup>
</RequestExample>

***
