Auto Portability Checker
Handle Pylon issues/tickets that request number portability checks for Telnyx.
Triggers
- A Pylon issue/ticket requesting a portability check
- Keywords: "portability check", "can we port", "is this number portable", "porting eligibility", "check if we support porting in [country]"
- Any Pylon ticket where the customer asks whether Telnyx can port their number(s) or whether a country is supported
Sender verification
Check message.author.user.email or message.email_info.from_email from the ticket messages to identify the sender. Process tickets from any sender.
Pylon API
Base URL: https://api.usepylon.com
Auth: Authorization: Bearer $PYLON_API_TOKEN
Token stored in: ~/.openclaw/workspace/france-rio-provider/.env (PYLON_API_TOKEN)
| Action |
Method |
Endpoint |
Notes |
| List issues |
GET |
/issues?start_time={ISO}&end_time={ISO}&limit=50 |
Required params: start_time, end_time (ISO 8601) |
| Get issue |
GET |
/issues/{id} |
Returns issue details including requester |
| Read messages |
GET |
/issues/{id}/messages |
Returns messages with author.user.email, email_info.from_email |
| Reply (customer-facing) |
POST |
/issues/{id}/reply |
Body: {"message_id": "...", "body_html": "...", "email_info": {"to_emails": ["..."]}} |
| Internal note |
POST |
/issues/{id}/note |
Body: {"body_html": "..."} — not visible to customer |
| Close ticket |
PATCH |
/issues/{id} |
Body: {"state": "closed"} |
Rate limits: Issues 10/min, Messages 20/min, Reply 10/min
Telnyx Portability API
Endpoint: POST https://api.telnyx.com/v2/portability_checks
Auth: Authorization: Bearer $TELNYX_API_KEY
Content-Type: application/json
Request body:
{
"phone_numbers": ["+3221234567", "+442071234567", "+61281234567"]
}
- All numbers must be in E.164 format (+ prefix, country code, no spaces)
- Batch supported — pass all numbers in one request
Response (201):
{
"data": [
{
"record_type": "portability_check_result",
"phone_number": "+3221234567",
"phone_number_type": "local",
"carrier_name": null,
"messaging_capable": false,
"portable": true,
"fast_portable": false,
"not_portable_reason": null,
"not_portable_reason_description": null
}
]
}
Response fields:
portable (boolean) — whether the number can be ported to Telnyx
not_portable_reason (string|null) — null if portable; reason code if not (e.g. "no_coverage", "invalid_phone_number")
not_portable_reason_description (string|null) — human-readable explanation (e.g. "We do not have coverage for this phone number.", "The phone number is invalid.")
fast_portable (boolean) — whether the number is FastPort eligible
phone_number (string) — the E.164 number this result is for
phone_number_type (string|null) — inferred type (e.g. "local", "mobile", null if unknown)
carrier_name (string|null) — current carrier name (usually null)
messaging_capable (boolean) — whether the number supports messaging
Error responses:
401 — Unauthorized (check API key)
422 — Unprocessable entity (check message field for details)
cURL example:
curl -X POST https://api.telnyx.com/v2/portability_checks \
-H "Authorization: Bearer $TELNYX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_numbers": ["+3221234567", "+3221234568"]}'
Workflow
Step 1 — Fetch and parse the Pylon ticket
- Fetch the ticket:
GET /issues/{id}
- Read messages:
GET /issues/{id}/messages
- Identify sender — check
message.author.user.email or message.email_info.from_email on the first customer message for use in the reply.
- Parse the ticket subject + body for:
- Phone number(s) — if present → Step 2 (number-based flow)
- Country name only — if no numbers → Step 3 (country-only flow)
- Record the
message_id from the first customer message (needed for reply)
Step 2 — Number-based flow
- Normalize all numbers to E.164 format.
- Infer country from the E.164 country code for each number.
- Look up country coverage in
references/porting-coverage.json.
- If the country is not in the file → draft ticket reply: "Telnyx does not currently support porting in [country]."
- If the country exists but the inferred number type (Local/National/Toll-Free/Mobile) is not supported → reply with what IS supported and stop. Do NOT call the portability API.
- Call the Telnyx portability API:
POST https://api.telnyx.com/v2/portability_checks
Authorization: Bearer $TELNYX_API_KEY
Content-Type: application/json
{"phone_numbers": ["+3221234567", "+3221234568"]}
- Interpret results — for each number in the response:
portable: true → ✅ Portable (include phone_number_type and fast_portable in reply)
portable: false → ❌ Not portable (include not_portable_reason_description)
- API error or missing number → ⚠️ Unable to determine — escalate
- Draft reply and post to the Pylon ticket via
POST /issues/{id}/reply.
- Close the ticket via
PATCH /issues/{id} with {"state": "closed"} — unless escalation is needed.
Step 3 — Country-only flow
- Look up the country in
references/porting-coverage.json (case-insensitive, also match slug).
- If not found → draft ticket reply listing the unsupported country/countries. If multiple countries are unsupported, list them together and add the expansion message once as a separate paragraph (not per country):
- Single unsupported country: "Telnyx does not currently support number porting in [country]. We're always expanding, and we hope that we will soon be able to port this type of number as we continue to expand our network. But we do not have an ETA."
- Multiple unsupported countries: "Telnyx does not currently support number porting in [country1], [country2], and [country3]. We're always expanding, and we hope that we will soon be able to port these types of numbers as we continue to expand our network. But we do not have an ETA." → post reply → close ticket.
- If found → draft ticket reply with:
- Supported number types (Local, National, Toll-Free, Mobile) with status and lead time
- Porting hours
- Porting requirements — always as bullet points, never as a single line. Each requirement gets its own line. Group by type if different requirements exist per type.
- Any special notes from the coverage data
- Support article link
- Post reply via
POST /issues/{id}/reply.
- Close ticket via
PATCH /issues/{id} with {"state": "closed"}.
- Do NOT call the Telnyx portability API.
Response format
Country-only example (Belgium)
Yes, Telnyx supports number porting in Belgium 🇧🇪.
Supported number types:
- Local: ✅ (4+ business days)
- National: ✅ (4+ business days)
- Toll-Free: ✅ (4+ business days)
- Mobile: ❌ Not supported
Porting hours: 8 AM – 5 PM local
Requirements (Local):
- LOA (local address required, area code must match)
- VAT / TAX ID
- Latest invoice
- Proof of local address
Requirements (National/Toll-Free):
- LOA (national address required)
- VAT / TAX ID
- Latest invoice
- Proof of local address
📖 Full guide: https://support.telnyx.com/en/articles/3266421-belgium-number-porting
Number-based example
Portability check for +3221234567 (Belgium 🇧🇪):
✅ +3221234567 — Portable
- Type: Local
- FastPort: No
- Country: Belgium
- Lead time: 4+ business days
- Porting hours: 8 AM – 5 PM local
If any numbers fail or are inconclusive, they will be listed separately with the reason.
Escalation
- When to escalate (do NOT close ticket):
- Telnyx portability API returns an error or inconclusive result
- Number type is supported at country level but the API cannot confirm portability
- Any unexpected response or ambiguity
- How to escalate:
- Post an internal note via
POST /issues/{id}/note explaining the situation
- Do NOT close the ticket — leave it open for a human agent
Formatting rules
Key rules
- Process tickets from any sender.
- Never assume country coverage means a specific number is portable. Always run the API check for number-based requests.
- Escalate on API failure — do not guess or infer portability without an API response.
- Always reply on the same Pylon ticket the request came from — use
POST /issues/{id}/reply.
- Always close the ticket after a successful reply — unless escalating.
- US/CA/PR are handled by the US Porting Team and are NOT in the coverage file. Redirect to: https://support.telnyx.com/en/articles/8673249-us-ca-toll-free-number-porting
- The coverage data file is
references/porting-coverage.json — 39 countries, 4 number types each.
- Phone numbers must be E.164 before calling the portability API.
- Use HTML in reply bodies (
body_html field) for formatting in Pylon.
TELNYX_API_KEY is stored in ~/.openclaw/workspace/france-rio-provider/.env.
References
references/global-porting-coverage.html — the source of truth. Updated HTML file with all 39 countries, number types, requirements, and carrier toggles.
references/porting-coverage.json — structured porting coverage data extracted from the HTML. 39 countries, 4 number types each. Includes per-country: supported types, lead times, porting hours, requirements, LOA links, support article links, and carrier lists for limited types (e.g. UK Mobile).
1---2name: auto-portability-checker3description: Check number porting coverage and per-number portability from Pylon tickets requesting portability checks.4---56# Auto Portability Checker78Handle Pylon issues/tickets that request number portability checks for Telnyx.910## Triggers1112- A Pylon issue/ticket requesting a portability check13- Keywords: "portability check", "can we port", "is this number portable", "porting eligibility", "check if we support porting in [country]"14- Any Pylon ticket where the customer asks whether Telnyx can port their number(s) or whether a country is supported1516## Sender verification1718Check `message.author.user.email` or `message.email_info.from_email` from the ticket messages to identify the sender. Process tickets from any sender.1920## Pylon API2122**Base URL:** `https://api.usepylon.com`23**Auth:** `Authorization: Bearer $PYLON_API_TOKEN`24**Token stored in:** `~/.openclaw/workspace/france-rio-provider/.env` (`PYLON_API_TOKEN`)2526| Action | Method | Endpoint | Notes |27|--------|--------|----------|-------|28| List issues | GET | `/issues?start_time={ISO}&end_time={ISO}&limit=50` | Required params: start_time, end_time (ISO 8601) |29| Get issue | GET | `/issues/{id}` | Returns issue details including `requester` |30| Read messages | GET | `/issues/{id}/messages` | Returns messages with `author.user.email`, `email_info.from_email` |31| Reply (customer-facing) | POST | `/issues/{id}/reply` | Body: `{"message_id": "...", "body_html": "...", "email_info": {"to_emails": ["..."]}}` |32| Internal note | POST | `/issues/{id}/note` | Body: `{"body_html": "..."}` — not visible to customer |33| Close ticket | PATCH | `/issues/{id}` | Body: `{"state": "closed"}` |3435**Rate limits:** Issues 10/min, Messages 20/min, Reply 10/min3637## Telnyx Portability API3839**Endpoint:** `POST https://api.telnyx.com/v2/portability_checks`40**Auth:** `Authorization: Bearer $TELNYX_API_KEY`41**Content-Type:** `application/json`4243**Request body:**44```json45{46 "phone_numbers": ["+3221234567", "+442071234567", "+61281234567"]47}48```49- All numbers must be in E.164 format (+ prefix, country code, no spaces)50- Batch supported — pass all numbers in one request5152**Response (201):**53```json54{55 "data": [56 {57 "record_type": "portability_check_result",58 "phone_number": "+3221234567",59 "phone_number_type": "local",60 "carrier_name": null,61 "messaging_capable": false,62 "portable": true,63 "fast_portable": false,64 "not_portable_reason": null,65 "not_portable_reason_description": null66 }67 ]68}69```7071**Response fields:**72- `portable` (boolean) — whether the number can be ported to Telnyx73- `not_portable_reason` (string|null) — `null` if portable; reason code if not (e.g. `"no_coverage"`, `"invalid_phone_number"`)74- `not_portable_reason_description` (string|null) — human-readable explanation (e.g. `"We do not have coverage for this phone number."`, `"The phone number is invalid."`)75- `fast_portable` (boolean) — whether the number is FastPort eligible76- `phone_number` (string) — the E.164 number this result is for77- `phone_number_type` (string|null) — inferred type (e.g. `"local"`, `"mobile"`, `null` if unknown)78- `carrier_name` (string|null) — current carrier name (usually `null`)79- `messaging_capable` (boolean) — whether the number supports messaging8081**Error responses:**82- `401` — Unauthorized (check API key)83- `422` — Unprocessable entity (check message field for details)8485**cURL example:**86```bash87curl -X POST https://api.telnyx.com/v2/portability_checks \88 -H "Authorization: Bearer $TELNYX_API_KEY" \89 -H "Content-Type: application/json" \90 -d '{"phone_numbers": ["+3221234567", "+3221234568"]}'91```9293## Workflow9495### Step 1 — Fetch and parse the Pylon ticket96971. Fetch the ticket: `GET /issues/{id}`982. Read messages: `GET /issues/{id}/messages`993. **Identify sender** — check `message.author.user.email` or `message.email_info.from_email` on the first customer message for use in the reply.1004. Parse the ticket subject + body for:101 - Phone number(s) — if present → Step 2 (number-based flow)102 - Country name only — if no numbers → Step 3 (country-only flow)1035. Record the `message_id` from the first customer message (needed for reply)104105### Step 2 — Number-based flow1061071. **Normalize** all numbers to E.164 format.1082. **Infer country** from the E.164 country code for each number.1093. **Look up country coverage** in `references/porting-coverage.json`.110 - If the country is not in the file → draft ticket reply: "Telnyx does not currently support porting in [country]."111 - If the country exists but the inferred number type (Local/National/Toll-Free/Mobile) is not supported → reply with what IS supported and stop. Do NOT call the portability API.1124. **Call the Telnyx portability API:**113 ```114 POST https://api.telnyx.com/v2/portability_checks115 Authorization: Bearer $TELNYX_API_KEY116 Content-Type: application/json117118 {"phone_numbers": ["+3221234567", "+3221234568"]}119 ```1205. **Interpret results** — for each number in the response:121 - `portable: true` → ✅ Portable (include `phone_number_type` and `fast_portable` in reply)122 - `portable: false` → ❌ Not portable (include `not_portable_reason_description`)123 - API error or missing number → ⚠️ Unable to determine — escalate1246. **Draft reply** and post to the Pylon ticket via `POST /issues/{id}/reply`.1257. **Close the ticket** via `PATCH /issues/{id}` with `{"state": "closed"}` — unless escalation is needed.126127### Step 3 — Country-only flow1281291. Look up the country in `references/porting-coverage.json` (case-insensitive, also match slug).1302. If not found → draft ticket reply listing the unsupported country/countries. If multiple countries are unsupported, list them together and add the expansion message once as a separate paragraph (not per country):131 - Single unsupported country: "Telnyx does not currently support number porting in [country]. We're always expanding, and we hope that we will soon be able to port this type of number as we continue to expand our network. But we do not have an ETA."132 - Multiple unsupported countries: "Telnyx does not currently support number porting in [country1], [country2], and [country3]. We're always expanding, and we hope that we will soon be able to port these types of numbers as we continue to expand our network. But we do not have an ETA." → post reply → close ticket.1333. If found → draft ticket reply with:134 - Supported number types (Local, National, Toll-Free, Mobile) with status and lead time135 - Porting hours136 - Porting requirements — **always as bullet points, never as a single line.** Each requirement gets its own line. Group by type if different requirements exist per type.137 - Any special notes from the coverage data138 - Support article link1394. Post reply via `POST /issues/{id}/reply`.1405. Close ticket via `PATCH /issues/{id}` with `{"state": "closed"}`.1416. Do NOT call the Telnyx portability API.142143## Response format144145### Country-only example (Belgium)146147> Yes, Telnyx supports number porting in Belgium 🇧🇪.148>149> **Supported number types:**150> - Local: ✅ (4+ business days)151> - National: ✅ (4+ business days)152> - Toll-Free: ✅ (4+ business days)153> - Mobile: ❌ Not supported154>155> **Porting hours:** 8 AM – 5 PM local156>157> **Requirements (Local):**158> - LOA (local address required, area code must match)159> - VAT / TAX ID160> - Latest invoice161> - Proof of local address162>163> **Requirements (National/Toll-Free):**164> - LOA (national address required)165> - VAT / TAX ID166> - Latest invoice167> - Proof of local address168>169> 📖 Full guide: https://support.telnyx.com/en/articles/3266421-belgium-number-porting170171### Number-based example172173> Portability check for +3221234567 (Belgium 🇧🇪):174>175> ✅ **+3221234567** — Portable176> - Type: Local177> - FastPort: No178> - Country: Belgium179> - Lead time: 4+ business days180> - Porting hours: 8 AM – 5 PM local181>182> If any numbers fail or are inconclusive, they will be listed separately with the reason.183184## Escalation185186- **When to escalate (do NOT close ticket):**187 - Telnyx portability API returns an error or inconclusive result188 - Number type is supported at country level but the API cannot confirm portability189 - Any unexpected response or ambiguity190- **How to escalate:**191 - Post an internal note via `POST /issues/{id}/note` explaining the situation192 - Do NOT close the ticket — leave it open for a human agent193194## Formatting rules195196- **Requirements must always be bullet points** — never compress into a single line. Each requirement gets its own bullet.197- Example:198 ```199 Requirements (all types):200 - LOA (national address mandatory)201 - SIRET code (14 digits for business)202 - RIO code (12 characters, dial 3179)203 - Latest invoice204 - Proof of address (within 3 months)205 ```206- If requirements differ by number type, group them under subheadings (e.g. "Requirements (Local):", "Requirements (Toll-Free):"), each with their own bullet list.207- For multiple unsupported countries, list them together in one sentence and add the expansion message once as a separate paragraph.208209## Key rules210211- Process tickets from any sender.212- **Never assume** country coverage means a specific number is portable. Always run the API check for number-based requests.213- **Escalate on API failure** — do not guess or infer portability without an API response.214- **Always reply on the same Pylon ticket** the request came from — use `POST /issues/{id}/reply`.215- **Always close the ticket** after a successful reply — unless escalating.216- **US/CA/PR** are handled by the US Porting Team and are NOT in the coverage file. Redirect to: https://support.telnyx.com/en/articles/8673249-us-ca-toll-free-number-porting217- The coverage data file is `references/porting-coverage.json` — 39 countries, 4 number types each.218- Phone numbers must be E.164 before calling the portability API.219- Use HTML in reply bodies (`body_html` field) for formatting in Pylon.220- `TELNYX_API_KEY` is stored in `~/.openclaw/workspace/france-rio-provider/.env`.221222## References223224- `references/global-porting-coverage.html` — the source of truth. Updated HTML file with all 39 countries, number types, requirements, and carrier toggles.225- `references/porting-coverage.json` — structured porting coverage data extracted from the HTML. 39 countries, 4 number types each. Includes per-country: supported types, lead times, porting hours, requirements, LOA links, support article links, and carrier lists for limited types (e.g. UK Mobile).