---
title: "从 Telserv 迁移到 Bird"
description: "将 Telserv 合作伙伴 API 集成迁移到 Bird API，将每组操作映射到对应的 Bird 调用，并了解每项任务在 Bird 中的完成方式。"
canonical: "https://bird.com/zh-sg/wendang/guides/numbers/migrate/telserv"
---

# 从 Telserv 迁移到 Bird

本指南适用于调用 Telserv 合作伙伴 API（V1 或 V2）来购买、路由和释放号码，并且现在要迁移到 Bird API 的集成。

Bird API 服务于所有 Bird 产品，因此其号码和语音操作遵循 Bird 自身的约定，而非 Telserv 的约定。您的集成使用的大多数功能都有对应的 Bird 调用。端点、载荷、标识符和身份验证方式均不同，因此请计划重写集成。当 Bird 以不同方式完成某项任务时，本指南会展示具体做法。

本指南仍在编写中。它涵盖了 Bird 已支持的内容，并随着迁移的推进而扩充。如果您使用的某个 Telserv 操作未在此列出，说明它仍需范围界定和开发才能添加；请向您的迁移联系人咨询。

## 开始之前

- 已完成身份验证的 Bird 组织，首次购买号码需要此验证。
- 一个具有示例所需权限范围的 [API 密钥](/docs/api/authentication)：`numbers` 的写入权限（用于号码和订单），`compliance` 的写入权限（用于独立的所有权文件操作），以及 `voice_management` 的写入权限（用于 SIP 中继、语音号码和已验证号码）。测试呼叫还需要 `voice:write`，读取呼叫段需要 `voice:read`。
- 钱包余额。订单从钱包中扣款。[当订单被拒绝时](/docs/guides/numbers/buying-numbers#when-an-order-is-refused)解释了 `402` 和 `412` 拒绝的原因。

您在 Telserv 中管理的号码无需重新购买。号码迁移到您的 Bird 工作区后，它们会出现在 [`GET /v1/numbers`](/docs/api/reference/list-workspace-numbers) 中。您的迁移联系人会确认迁移完成的时间。要获取已迁移号码的 `nda_` 标识符，请使用 `GET /v1/numbers?number=<digits>` 查找，输入带国家代码的号码，并将 `id` 保存为 `NUMBER_ID`。

示例从 `BIRD_API_KEY` 读取您的密钥，从 `BIRD_API_URL` 读取其[区域主机](/docs/api/regions)：`bk_eu1_` 密钥使用 `https://eu1.platform.bird.com`，`bk_us1_` 密钥使用 `https://us1.platform.bird.com`。其他变量（如 `TRUNK_ID` 和 `NUMBER_ID`）保存的是前一个响应中的标识符；每个步骤会说明它设置的变量。

## 接口约定的变化

| 方面     | Telserv 合作伙伴 API                                                      | Bird API                                                                                                                                                                                              |
| -------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 认证     | `X-ApiKey` 请求头（V2）或 `apiKey` 参数（V1），根据您的 IP 白名单进行校验 | 使用 `Authorization: Bearer` 搭配一个 API 密钥，其权限范围指定了可访问的产品领域。IP 白名单为可选项：在 [**API keys**](https://bird.com/dashboard/w/api-keys) 中的密钥上通过 **IP restrictions** 设置 |
| 标识符   | 整数。购买号码时会为其分配一个新的 `id`                                   | 带前缀的 TypeID，如 `nda_`、`nor_`、`vnu_` 和 `spt_`。号码在分配给您期间保持其 `nda_` id 不变                                                                                                         |
| 电话号码 | 不带前导 `+` 的数字                                                       | 带前导 `+` 的 E.164 格式                                                                                                                                                                              |
| 分页     | `start` 和 `limit` 偏移量，每页最多 1000 条                               | 游标分页：`limit` 最大 100（默认 25），然后将 `starting_after` 设为上一页的 `next_cursor`。参见[分页](/docs/api/pagination)                                                                           |
| 错误     | `errorCode` 表示异常类名，`403` 涵盖资源缺失和许多校验失败                | `error.code` 是稳定的 `E` 代码，附带文档页面，在已知恢复方式时还附带 `remediation`。资源缺失返回 `404`，前提条件不满足返回 `412`，输入无效返回 `422` 并附字段详情。参见[错误](/docs/guides/errors)    |
| 环境     | LIVE 和 SANDBOX，各有独立密钥                                             | 单一环境。每笔订单购买并扣费一个真实号码，每次测试通话都是真实通话                                                                                                                                    |
| 重试     | 无幂等键                                                                  | 写入操作使用 `Idempotency-Key` 请求头。相同的键和请求体会以 `Idempotency-Replay: true` 重放首次响应；相同的键搭配不同的请求体则返回 `409`。参见[幂等性](/docs/guides/idempotency)                     |

Bird API 会以 `422` 和 [`E01029`](/docs/api/errors/E01029) 拒绝无法识别的查询参数。请从请求中移除 `city` 等 Telserv 专有过滤器，而不是直接传递它们。

## 一个号码，两个标识符

分配到您工作区的语音号码有两条记录，各自拥有独立的标识符：

- 分配记录，由 [`GET /v1/numbers`](/docs/api/reference/list-workspace-numbers) 返回。其 `id` 对专属号码以 `nda_` 开头，对共享号码以 `nal_` 开头；订单的 `number_id` 是所购号码的 `nda_` 标识符。用它来读取、命名和引用号码，以及释放专属号码。
- 语音记录 `vnu_`，由 [`GET /v1/voice/numbers`](/docs/api/reference/list-voice-numbers) 返回。用它来读取和更改号码来电的路由目标。没有语音功能的号码（例如仅支持 SMS 的号码）没有语音记录。

语音操作会以 `422` 拒绝 `nda_` 或 `nal_` 标识符。要查找号码的语音记录，请将 `search` 设为号码的数字来列出语音号码，然后取 `provider.number_id` 等于分配记录 `id` 的条目。[将号码路由到您的 SIP 中继](#将号码路由到您的-sip-中继)展示了该请求，[管理您的电话号码](/docs/guides/numbers/managing-numbers#find-the-voice-record-that-routes-a-number)介绍了查找方法。

Telserv 的 `GET /numbers/{dnisId}` 在 Bird 中变为两次读取：分配记录用于状态和所有权文件，语音记录用于路由。

## 将 Telserv 操作映射到 Bird

每张表列出一项任务、执行该任务的 Telserv 调用，以及现在执行该任务的 Bird 调用。表下方的说明介绍请求或响应中的变化。

### 购买

| 任务         | Telserv                                                  | Bird                                                                             |
| ------------ | -------------------------------------------------------- | -------------------------------------------------------------------------------- |
| 搜索在售号码 | `GET /shop`                                              | [`GET /v1/numbers/available`](/docs/api/reference/list-available-numbers)        |
| 查询单个号码 | `GET /shop/{stockDnisId}`                                | [`GET /v1/numbers/available/{number}`](/docs/api/reference/get-available-number) |
| 购买号码     | `POST /shop/{stockDnisId}`                               | [`POST /v1/numbers/orders`](/docs/api/reference/create-numbers-order)            |
| 锁定号码     | `POST /shop/{stockDnisId}/reserve`、`GET /shop/reserved` | [`POST /v1/numbers/orders`](/docs/api/reference/create-numbers-order)            |

- **搜索。** `country_code` 为必填项，因此一次搜索只覆盖一个国家。`prefix` 接受国家代码后的国内号码：Telserv 的 `prefix=3170` 对应为 `country_code=NL&prefix=70`。使用 `prefix` 代替 `city` 按区号筛选，使用 `number_type` 和 `capabilities` 代替 `productId` 进行筛选。
- **搜索结果中的所有权文件。** 每条结果包含 `ownership_registration_required`，替代 `excludeNumbersRequireVerification` 筛选条件。`true` 值表示该号码从购买时开始计费，但在其所有权文件审核通过之前，不会发送 SMS，也无法承载呼入或呼出语音通话。
- **查询单个号码。** 通过其 E.164 号码进行查询。
- **价格。** Telserv 在号码和产品的详细查询中返回 `pricing.staticPricing`。在 Bird 中，每条搜索结果在 `pricing` 中包含您组织的价格：`setup_price` 替代 `nrc`，`recurring_price` 每 `recurring_period` 替代 `mrc`，`intervals_per_charge` 表示一次收费涵盖多少个计费周期。[使用您的引用标识搜索并购买号码](#使用您的引用标识搜索并购买号码)介绍了价格字段。
- **购买。** 发送 `number` 和可选的 `reference`。订单从您的钱包中扣款。大多数订单返回 `201`；等待运营商处理的订单返回 `202`，需要轮询其状态。
- **锁定。** 下单即锁定该号码。先查询候选号码，然后下单。

### 号码

| 任务         | Telserv                                         | Bird                                                                                                                                                                                                                                |
| ------------ | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 列出号码     | `GET /numbers`                                  | [`GET /v1/numbers`](/docs/api/reference/list-workspace-numbers)                                                                                                                                                                     |
| 读取单个号码 | `GET /numbers/{dnisId}`                         | [`GET /v1/numbers/{number_id}`](/docs/api/reference/get-workspace-number)，使用分配记录的 `nda_` 或 `nal_` id；以及 [`GET /v1/voice/numbers/{number_id}`](/docs/api/reference/get-voice-number)，使用语音记录的 `vnu_` id           |
| 变更号码     | `PATCH /numbers/{dnisId}`                       | [`PATCH /v1/numbers/{number_id}`](/docs/api/reference/update-workspace-number)，使用分配记录的 `nda_` 或 `nal_` id；以及 [`PATCH /v1/voice/numbers/{number_id}`](/docs/api/reference/update-voice-number)，使用语音记录的 `vnu_` id |
| 释放号码     | `DELETE /numbers/{dnisId}?requestedCancelDate=` | 在已付费周期结束时 [`DELETE /v1/numbers/{number_id}`](/docs/api/reference/cancel-workspace-number)，或立即 [`POST /v1/numbers/{number_id}/release`](/docs/api/reference/release-workspace-number)，两者均使用分配记录的 `nda_` id   |

- **列表。** 列表中包含当前分配给您的号码。在 Bird 中订购后已释放的号码仍可通过 [`GET /v1/numbers/orders`](/docs/api/reference/list-numbers-orders) 中的订单查看。[核对您的号码](#核对您的号码)列出了筛选条件的映射关系。
- **读取。** 状态、所有权文件和价格在分配记录上，路由在语音记录上。分配记录的 `pricing` 对象（含每 `recurring_period` 的 `recurring_price` 和 `intervals_per_charge`）取代了 `pricing.staticPricing.mrc`，且列表也包含该信息，而 Telserv 的 `GET /numbers` 则不包含。
- **变更。** `name` 和 `reference` 在分配记录上，路由在语音记录上。在您的 SIP 设备上设置强制编解码器和并发呼叫上限。`destinationDistributionType` 变为 SIP 中继上的网关 `priority`：`FailOver` 为每个网关分配不同的 `priority`，`RoundRobin` 则为它们分配相同的值。
- **释放。** `DELETE` 保留号码直到您已付费周期结束后再释放。`POST …/release` 立即释放号码。两者都不接受日期参数。[释放号码](#释放号码)展示了两种请求。

### 目标地址和配置文件

| 任务                  | Telserv                                                     | Bird                                                                                                                                                           |
| --------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 将呼叫发送到 SIP 设备 | `POST /numbers/{dnisId}/destinations`                       | [`POST /v1/voice/trunks/{trunk_id}/gateways`](/docs/api/reference/create-voice-trunk-gateway)，然后路由 `trunk`                                                |
| 将呼叫转接到电话      | `POST /numbers/{dnisId}/destinations`                       | 路由 `forward`                                                                                                                                                 |
| 停止接听来电          | `DELETE /numbers/{dnisId}/destinations`                     | 路由 `reject`                                                                                                                                                  |
| 读取目标配置文件      | `GET /destinationprofiles`、`GET /destinationprofiles/{id}` | [`GET /v1/voice/trunks`](/docs/api/reference/list-voice-trunks) 和 [`GET /v1/voice/trunks/{trunk_id}/gateways`](/docs/api/reference/list-voice-trunk-gateways) |
| 将配置文件应用到号码  | `POST /numbers/{dnisId}/destinations/profile`               | 使用 `trunk_id` 路由 `trunk`                                                                                                                                   |

- **SIP 设备。** 网关属于中继，为路由到该中继的每个号码提供服务，因此需要不同目标列表的号码需要不同的中继。`sortOrder` 变为 `priority`。在您的 SIP 设备上设置振铃时间和时间窗口。
- **转发。** 设置 `forward_to` 和 `forward_as`。`forward_to` 必须是您的[已验证号码](/docs/guides/voice/caller-ids)之一；未验证的号码会返回 `412` 和 [`E21053`](/docs/api/errors/E21053)。`forward_as` 选择转发来电显示的主叫号码。一个号码同一时间只能有一条路由，因此如果需要从 SIP 设备回退到电话号码，请从您的 SIP 设备转发来电。
- **停止接听来电。** 该号码会拒绝来电，直到您重新为其设置路由。如果需要用常设的默认路由代替默认配置文件，请将该号码路由到包含您默认网关的中继。
- **配置文件。** `distributionType` 变为 `priority`：`FailOver` 为每个网关分配不同的 `priority`，`RoundRobin` 则为它们分配相同的值。分配策略按中继设置，因此需要不同分配策略的号码需要不同的中继。`{dnis}` 占位符变为 `destination_format` 中的 `{number}`，`prefixCLIP` 变为 `origination_format` 中 `{number}` 之前的文本。
- **应用配置文件。** 中继通过其 `spt_` 标识符寻址，而 Telserv 通过名称寻址配置文件。

### 文档和终端用户数据

| 任务             | Telserv                                                         | Bird                                                                                                                                                                                            |
| ---------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 查看要求         | `GET /products/conditions`、`GET /masterdata/productconditions` | 针对该号码使用 [`GET /v1/compliance/requirements`](/docs/api/reference/get-workspace-compliance-requirements)；或使用 `program` 且不带 `subject_id`，查看某个国家在您持有号码之前要求填写的表单 |
| 上传文档         | `POST /numbers/{dnisId}/documents`                              | [`POST /v1/compliance/attachments`](/docs/api/reference/create-compliance-attachment)，然后在提交中引用该附件。参见[提交所有权文件](#提交所有权文件)                                            |
| 发送终端用户数据 | `POST /numbers/{dnisId}/enduserdata`                            | [`POST /v1/compliance/submissions`](/docs/api/reference/create-workspace-compliance-submission)，一次请求中同时提交这些数据和文档。参见[提交所有权文件](#提交所有权文件)                        |
| 替换文档         | 再次调用 `POST /numbers/{dnisId}/documents`                     | [`POST /v1/compliance/submissions/{submission_id}/resupply`](/docs/api/reference/update-workspace-compliance-submission)，提交被拒绝或已过期的答复                                              |
| 查看文件审核进度 | `GET /numbers/{dnisId}/documents`                               | [`GET /v1/numbers/{number_id}`](/docs/api/reference/get-workspace-number) 上的 `ownership`，以及提交记录中每项答复的审核结论                                                                    |

- **要求。** Telserv 的条件目录一次涵盖所有产品；Bird 一次读取一份表单。要查询单个号码的要求，请使用 `subject_kind=number`，将其 `nda_` 标识符作为 `subject_id`，并提供其 `country_code`；或在购买前读取表单，将 `program` 设为号码类型且不提供 `subject_id`。`fields` 中的每个条目取代一个 Telserv 条件或终端用户标签：其 `key` 是您作答时使用的键，`data_type` `document` 表示该条目需要上传文件。
- **文档。** 以 `multipart/form-data` 上传文件，代替 base64 `fileContent`；[上传每份文档](/docs/guides/numbers/ownership-paperwork#upload-each-document)列出了接受的文件。重复上传同一文件会返回同一附件。仅上传不会提交任何内容：引用其 `attachment_id` 的提交才会提交。
- **同时提交终端用户数据和文档。** Telserv 每份文档单独调用一次，终端用户数据则作为一个标签列表在另一次调用中提交。Bird 在一次提交中接收所有答复（包括填写的数据和文档），且没有草稿：该请求会直接提交答复供审核。之后请跟踪 `ownership.next`，它会指明需要更正时使用补充提交，仅在必要时才需要新的提交（[提交所有答复](/docs/guides/numbers/ownership-paperwork#submit-every-answer)）。
- **替换。** 只发送您要替换的答复，其他答复保留各自的审核状态。Telserv 在号码待验证期间接受某个条件的新文件，并重置该条件的审核；Bird 可替换被拒绝、已过期或尚未提供的答复，但会拒绝仍在审核中或已通过的答复，除非在我们发起的更正请求未关闭期间，该答复被标记为 `editable_for_verifier_repair`（[更正被拒绝的答复](/docs/guides/numbers/ownership-paperwork#correct-a-rejected-answer)）。
- **进度。** `ownership.status` 和 `ownership.next` 报告号码所处的状态。每项答复都有自己的 `state`，被拒绝的答复附带审核人员的 `reason`。Bird 还会通过电子邮件将每个结果发送给您组织的所有者，以及最近一次提交或更正该提交的人员。当号码无需提交文件时，`ownership` 通常为 `null`。

[提交所有权文件](#提交所有权文件)展示了相关请求，[提交号码的所有权文件](/docs/guides/numbers/ownership-paperwork)介绍了完整流程，包括通过控制台提交以及随订单一起提交。

### 产品与主数据

| 任务     | Telserv                                      | Bird                                                                                                                                                                                                                                                                                        |
| -------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 浏览产品 | `GET /products`、`GET /products/{productId}` | [`GET /v1/numbers/available`](/docs/api/reference/list-available-numbers)，需要提供 `country_code`，并返回该国家在售的号码。Bird 没有全球产品目录，也没有与 `productId` 对应的内容。没有任何 API 列出在售的国家和号码类型；[电话号码定价](/pricing/numbers)页面按国家列出 Bird 的标准价格。 |

每条搜索结果用 `number_type`、`capabilities` 和 `ownership_registration_required` 描述其产品，并携带您组织的价格，代替产品的 `pricing.staticPricing`（`nrc` 和 `mrc`）。Bird 的标准价格在[电话号码定价](/pricing/numbers)页面上。Telserv 的按分钟 `sessionPricing` 在这些读取中没有对应项；语音费率在[语音定价](/pricing/voice)页面上。

Telserv 的号码类型与 Bird 的 `number_type` 对应关系如下：

| Telserv `numberType` | Bird `number_type`                |
| -------------------- | --------------------------------- |
| `DID`                | `local` 或 `national`，取决于号码 |
| `FreePhone`          | `toll_free`                       |
| `Mobile`             | `mobile`                          |

## 实现方式不同的任务

以下 Telserv 任务在 Bird 中的实现方式有所不同。在迁移依赖其中某项功能的集成之前，请提前做好规划。

- **所有权文件。** 在一次提交中提交号码的文档和终端用户数据，不支持草稿；被拒绝的答案通过补充提交（resupply）进行更正。
- **预留。** 选定号码后直接下单；下单即锁定该号码。
- **单号码通话设置。** 在您的 SIP 设备上设置强制编解码器、并发通话上限、振铃时间和时间窗口。
- **释放日期。** Bird 在已付费周期结束时或立即释放号码，不支持指定日期。请在您自己一侧保留之前发送给 Telserv 的取消日期，并在到期时执行操作。
- **钱包余额。** 在控制台中查看余额并充值。

## 使用您的引用标识搜索并购买号码

搜索一个国家。组合 `number_type`、`capabilities` 和 `prefix` 来缩小结果范围：

```bash
curl "$BIRD_API_URL/v1/numbers/available?country_code=NL&number_type=mobile&capabilities=voice&limit=10" \
  --header "Authorization: Bearer $BIRD_API_KEY"
```

每条结果包含一个 `pricing` 对象，其中是您的组织为该号码支付的价格，为税前金额并以您的账单货币计价，已应用您与 Bird 协商的任何价格：

- `setup_price` 在您下单购买号码时一次性收取。公布的开通费为零时，其 `amount` 为 `"0"`。无开通费时该字段本身为 `null`；当 `recurring_price` 为 `null` 时也为该值，因为该号码无法订购。
- `recurring_price` 是每 `recurring_period` 的费率。
- `intervals_per_charge` 是一次收费所计费的 `recurring_period` 数量。税前周期费用为 `recurring_price` 乘以 `intervals_per_charge`，在号码分配到您的工作区时以及每次续费时收取；开通费另计。对于等待运营商处理的订单，号码在订单完成时分配。

[API 参考文档](/docs/api/reference/list-available-numbers)展示了一个示例结果。包含 `pricing.recurring_price: null` 的结果未提供周期性价格。请在下单前[联系销售](/contact-sales)，为您获取该国家和号码类型的定价。

控制台和 API 使用相同的组织价格，包括换算为您账单货币的金额。读取价格不会锁定报价：在后续收费之前，资费或汇率的变动可能改变该笔收费的金额。如果价格查询失败，搜索会返回 `503` 和 [`E14009`](/docs/api/errors/E14009)。请使用指数退避重试读取；查询失败绝不意味着该号码不收费。

要在下单前确认候选号码及其价格，请使用 `GET /v1/numbers/available/{number}` 读取该号码。`404` 表示该号码已不再出售。

使用您在 Telserv 中使用的引用标识下单购买号码，并选择一个幂等键，在本次购买的任何重试中继续使用：

```bash
curl --request POST "$BIRD_API_URL/v1/numbers/orders" \
  --header "Authorization: Bearer $BIRD_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: order-CUST-778812" \
  --data '{"number": "+31612345678", "reference": "CUST-778812"}'
```

`201` 响应包含 `status: completed` 和一个 `number_id`，即号码的 `nda_` 标识符。将其保存为 `NUMBER_ID`。`202` 响应表示订单正在等待运营商处理：[轮询订单](/docs/guides/numbers/buying-numbers#poll-an-order-that-did-not-finish)，直到其 `status` 变为 `completed` 或 `failed`。如果订单最终失败，已收取的开通费不予退还。引用标识在订单完成时设置到号码上。引用标识最多支持 100 个字符，不需要唯一。

对工作区已分配的号码重试下单会返回现有订单，因此重试不会重复扣费。[购买和释放号码](/docs/guides/numbers/buying-numbers#when-an-order-is-refused)解释了各种拒绝情况。

要稍后设置或更改引用标识，请将 `reference`（以及可选的 `name`）发送到 `PATCH /v1/numbers/{number_id}`。发送 `null` 可清除字段。

## 检查号码是否可以承载通话

`status` 为 `pending_ownership_registration` 的号码已被分配并计费，但在其所有权文件审核通过之前，它不会发送 SMS，并会拒绝所有来电和去电语音通话。在为号码配置路由或对其发起测试呼叫之前，请使用 [`GET /v1/numbers/{number_id}`](/docs/api/reference/get-workspace-number) 读取该号码，并检查 `status` 是否为 `active`。如果状态为待审核，请[提交所有权文件](#提交所有权文件)。[已分配号码的状态](/docs/guides/numbers/overview#the-status-of-an-allocated-number)解释了每个值的含义。

以下示例使用一个荷兰手机号码。无论号码属于哪个国家，请以相同方式检查您自己号码的 `status`。

## 提交所有权文件

Telserv 接收号码所有权文件的方式是每份文档一次 `POST /numbers/{dnisId}/documents` 调用，所有标签一次 `POST /numbers/{dnisId}/enduserdata` 调用。Bird 则在您上传文档后，通过一次提交同时接收两者。

1. 读取号码的 `country_code` 并将其保存为 `COUNTRY_CODE`。该命令使用 `jq` 提取该值；如果您不使用 `jq`，请手动读取：

   ```bash
   COUNTRY_CODE=$(curl -s "$BIRD_API_URL/v1/numbers/$NUMBER_ID" \
     --header "Authorization: Bearer $BIRD_API_KEY" | jq -r .country_code)
   ```

2. 读取号码所在国家的要求。`fields` 中的每个条目包含您作答时使用的 `key`、一个 `data_type`，以及表示是否必须作答的 `required`。将响应中给出的 `program` 保存为 `PROGRAM`：

   ```bash
   curl "$BIRD_API_URL/v1/compliance/requirements?subject_kind=number&subject_id=$NUMBER_ID&country_code=$COUNTRY_CODE" \
     --header "Authorization: Bearer $BIRD_API_KEY"
   ```

   返回 `422` 和 [`E19001`](/docs/api/errors/E19001) 表示该国家没有所有权表单。

3. 上传每个 `document` 字段要求的文档，以文件形式上传，而不是使用 base64 `fileContent`。将响应中的 `id`（即附件的 `tca_` 标识符）保存为 `ATTACHMENT_ID`：

   ```bash
   curl --request POST "$BIRD_API_URL/v1/compliance/attachments" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --form "file=@registration-certificate.pdf"
   ```

4. 一次提交所有答案：每个 `required` 为 `true` 的字段各一个条目，您有真实答案的每个可选字段也各一个条目；终端用户数据作为 `value`，每份文档作为 `attachment_id`。不要为可选字段编造值，直接省略即可。示例展示了两个答案；发送前请补全其余必填答案。此请求会提交答案以供审核。将响应中的 `id`（即提交记录的 `csb_` 标识符）保存为 `SUBMISSION_ID`：

   ```bash
   curl --request POST "$BIRD_API_URL/v1/compliance/submissions" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --header "Content-Type: application/json" \
     --header "Idempotency-Key: ownership-CUST-778812" \
     --data '{"subject_kind": "number", "subject_id": "'"$NUMBER_ID"'", "program": "'"$PROGRAM"'", "country_code": "'"$COUNTRY_CODE"'", "answers": [{"key": "business_registration_number", "value": "90012768"}, {"key": "business_registration_certificate", "attachment_id": "'"$ATTACHMENT_ID"'"}]}'
   ```

   `201` 响应返回提交记录。`422` 表示未提交任何内容：请更正请求体，并使用新的幂等键发送，因为原来的键会重放该拒绝结果。[提交所有答案](/docs/guides/numbers/ownership-paperwork#submit-every-answer)解释了每种拒绝情况。

5. 当审核人员拒绝某个答案时，只需替换该答案，具体方法见[更正被拒绝的答案](/docs/guides/numbers/ownership-paperwork#correct-a-rejected-answer)：

   ```bash
   curl --request POST "$BIRD_API_URL/v1/compliance/submissions/$SUBMISSION_ID/resupply" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --header "Content-Type: application/json" \
     --header "Idempotency-Key: ownership-fix-1-CUST-778812" \
     --data '{"answers": [{"key": "business_registration_number", "value": "90012769"}]}'
   ```

要在购买时一并提交，请在 `POST /v1/numbers/orders` 的 `compliance` 块中发送相同的 `answers`。该订单使用现有的 `numbers` 写入权限范围，即使订单附带所有权文件也是如此。

使用 [`GET /v1/numbers/{number_id}`](/docs/api/reference/get-workspace-number) 上的 `ownership.status` 跟踪审核进度。这些回答是以您企业名义作出的法律陈述，因此只提交已由相关负责人确认的回答。[提交号码的所有权文件](/docs/guides/numbers/ownership-paperwork)介绍了每个请求、审核流程以及随订单提交的方式。

## 将号码路由到您的 SIP 中继

新号码没有自己的路由，因此遵循您工作区的[默认路由](/docs/guides/voice/receiving-calls#set-a-default-route)。该默认路由初始为 `reject`，但如果工作区已经设置了默认路由，新号码一经分配就会按该路由接听来电。要将来电投递到您的设备，请创建一个开启入站呼叫的 SIP 中继，为其添加网关，然后将号码指向该中继。[SIP 中继](/docs/guides/voice/sip-trunks)介绍了如何将您的设备连接到中继。

1. 创建中继并开启入站呼叫。保留响应中的 `id`，即中继的 `spt_` 标识符，存为 `TRUNK_ID`：

   ```bash
   curl --request POST "$BIRD_API_URL/v1/voice/trunks" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --header "Content-Type: application/json" \
     --header "Idempotency-Key: trunk-production-pbx" \
     --data '{"name": "Production PBX", "inbound_enabled": true}'
   ```

2. 添加网关。默认情况下，网关接收的被叫号码和主叫号码为 E.164 格式，带有 `+`。如果您的设备需要不带 `+` 的号码（如 Telserv 的 `{dnis}` 占位符），请将被叫号码的 `destination_format` 和主叫号码的 `origination_format` 设置为 `{number}`：

   ```bash
   curl --request POST "$BIRD_API_URL/v1/voice/trunks/$TRUNK_ID/gateways" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --header "Content-Type: application/json" \
     --header "Idempotency-Key: gateway-pbx-primary" \
     --data '{"sip_uri": "sip:pbx.example.com:5060", "priority": 0, "destination_format": "{number}", "origination_format": "{number}"}'
   ```

   如需故障转移，请添加第二个网关；[网关所需的配置](/docs/guides/voice/receiving-calls#what-a-gateway-needs)说明了 `priority` 和号码格式。在没有幂等键的情况下再次创建相同的网关会返回 `409` 和 [`E21047`](/docs/api/errors/E21047)。

3. 查找号码的语音记录。使用号码的数字进行搜索，取 `provider.number_id` 等于分配记录 `id` 的条目的 `id`。将其存为 `VOICE_NUMBER_ID`：

   ```bash
   curl "$BIRD_API_URL/v1/voice/numbers?search=31612345678" \
     --header "Authorization: Bearer $BIRD_API_KEY"
   ```

4. 将号码指向中继：

   ```bash
   curl --request PATCH "$BIRD_API_URL/v1/voice/numbers/$VOICE_NUMBER_ID" \
     --header "Authorization: Bearer $BIRD_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"inbound_configuration": {"route": {"type": "trunk", "trunk_id": "'"$TRUNK_ID"'"}}}'
   ```

   `200` 响应显示新路由。入站呼叫关闭的中继会返回 `412` 和 [`E21052`](/docs/api/errors/E21052)。

关闭中继的入站呼叫或删除中继，会将路由到该中继的每个号码恢复为您工作区的默认路由，重新开启入站呼叫不会恢复这些路由。[将呼叫送达 SIP 中继](/docs/guides/voice/receiving-calls#deliver-calls-to-a-sip-trunk)对此以及 `trunk`、`forward` 和 `reject` 路由进行了说明。

## 核对您的号码

使用最大页面大小遍历列表。将每页的 `next_cursor` 作为 `NEXT_CURSOR` 传入以读取下一页，直到 `next_cursor` 为 `null`。列表不返回总数。

```bash
curl "$BIRD_API_URL/v1/numbers?limit=100" \
  --header "Authorization: Bearer $BIRD_API_KEY"

curl "$BIRD_API_URL/v1/numbers?limit=100&starting_after=$NEXT_CURSOR" \
  --header "Authorization: Bearer $BIRD_API_KEY"
```

Telserv 的列表过滤器对应到 Bird 的方式如下：

| Telserv `GET /numbers` 上的过滤器       | `GET /v1/numbers` 上的 Bird 过滤器                                                                                                    |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `number`，匹配号码开头                  | 不带 `country_code` 的 `prefix`，从完整号码开头匹配数字                                                                               |
| `prefix`，等于接入码和区号，例如 `3170` | `country_code`，将 `prefix` 设为国内号码数字：`country_code=NL&prefix=70`                                                             |
| `reference`，使用部分值                 | `search`，匹配号码、名称或引用标识的部分内容，不区分大小写                                                                            |
| `reference`，使用引号括起的精确值       | `reference`，返回具有该精确引用标识的号码。匹配区分大小写                                                                             |
| Bird 中新增                             | `number` 返回一个带国家代码的完整号码，带或不带 `+` 或 `00` 均可：`+31612345678`、`31612345678` 和 `0031612345678` 查找的是同一个号码 |

[在列表中查找号码](/docs/guides/numbers/managing-numbers#find-a-number-in-your-list)详细介绍了每个过滤器。

每条记录包含 `pricing`，其中有每 `recurring_period` 的 `recurring_price` 和 `intervals_per_charge`。除非号码的价格已固定，否则 `recurring_price` 是您所在组织的当前费率，每次续费按续费时的有效费率收取，因此已计费的金额可能与之不同。[列出您持有的所有号码](/docs/guides/numbers/managing-numbers#list-every-number-you-hold)说明了每个号码的计费货币、`pricing` 或其价格何时为 `null`，以及设置了 `pricing.error` 时应如何处理。

已释放的号码会从此列表中消失，通过 `number_id` 读取它会返回 `404`。如果您通过 Bird 订购了该号码，其已完成的订单仍保留在 [`GET /v1/numbers/orders`](/docs/api/reference/list-numbers-orders) 中。

## 释放号码

两个请求都针对专属号码，使用其 `nda_` 标识符；共享号码会被拒绝，返回 [`E14002`](/docs/api/errors/E14002)。要保留号码直到已付费周期结束，请取消它：

```bash
curl --request DELETE "$BIRD_API_URL/v1/numbers/$NUMBER_ID" \
  --header "Authorization: Bearer $BIRD_API_KEY" \
  --header "Idempotency-Key: cancel-CUST-778812"
```

`202` 响应返回带有 `releases_at`（已付费周期结束时间）的号码。在此之前号码保持当前状态，订阅不再续费，届时号码将被释放。再次请求会返回相同的 `releases_at`，且取消操作不可撤销。没有订阅的号码会立即释放，响应为 `200`，号码的 `status` 设为 `released`。

要立即释放号码（包括已安排取消的号码）：

```bash
curl --request POST "$BIRD_API_URL/v1/numbers/$NUMBER_ID/release" \
  --header "Authorization: Bearer $BIRD_API_KEY" \
  --header "Idempotency-Key: release-CUST-778812"
```

`204` 响应表示号码已释放，立即生效。当前周期的剩余部分不予退款。

当号码的续费款项逾期未付时，两个请求都会返回 `409` 和 [`E03015`](/docs/api/errors/E03015)，直到付款完成。

两个请求都不接受日期参数，因此请在您这边保留之前作为 `requestedCancelDate` 发送给 Telserv 的时间表。要保留号码到指定日期，请在该日期落入当前计费周期后取消号码：号码会在该周期结束时释放，即在指定日期当天或之后。要在指定日期准确放弃号码，请在当天释放它。[释放号码](/docs/guides/numbers/managing-numbers#release-a-number)介绍了每个请求对号码、费用和重试的影响。

## 向您的号码发起测试呼叫

要端到端检查路由，请从您的工作区可用作来电显示的另一个号码向该号码发起呼叫。该号码所在的国家必须在您的[语音目的地](/docs/guides/voice/destinations)中启用，否则呼叫无法接通。下面的请求运行一个内联序列，在呼叫接通后立即挂断，因此不需要已保存的序列。该请求会发起真实呼叫，正常通话费用适用。

将请求保存为 `test-call.json`。将您要显示的来电号码放在 `from` 中（您的工作区拥有的或[已验证的号码](/docs/guides/voice/caller-ids)），将被测号码放在 `to` 中：

```json
{
  "from": "+31612345679",
  "to": "+31612345678",
  "sequence": {
    "definition": {
      "schema_version": 1,
      "expression_environment": "bird.cel.v1",
      "nodes": [
        {
          "id": "entry",
          "type": "trigger.start_call",
          "type_version": 1,
          "config": {},
          "input": {},
          "connections": { "event": { "node_id": "end", "port": "input" } }
        },
        { "id": "end", "type": "voice.hangup", "type_version": 1, "config": {}, "input": {} }
      ]
    },
    "entry_node_id": "entry",
    "trigger_data": {}
  }
}
```

发送请求：

```bash
curl --request POST "$BIRD_API_URL/v1/voice/calls" \
  --header "Authorization: Bearer $BIRD_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: ${TEST_CALL_KEY:?Set TEST_CALL_KEY once for this test call}" \
  --data-binary @test-call.json
```

每次测试将 `TEST_CALL_KEY` 设置为新值，在超时或结果不明确后重试同一测试时复用该值：三小时内相同的密钥和请求会重放首次接受结果，而不会发起第二次呼叫。`202` 响应表示呼叫已被接受，并返回其 `initial_leg_id`。接受并不证明呼叫已接通：使用 [`GET /v1/voice/legs/{leg_id}`](/docs/api/reference/get-voice-leg) 读取呼叫段，或使用 [`GET /v1/voice/legs`](/docs/api/reference/list-voice-legs) 及其 `to` 过滤器列出拨往该号码的呼叫段，并检查您的设备是否收到了呼叫。[创建语音呼叫](/docs/guides/voice/create-calls)介绍了来电显示和目的地要求以及重试规则。

## 后续步骤

- [购买和释放号码](/docs/guides/numbers/buying-numbers)
- [管理您的电话号码](/docs/guides/numbers/managing-numbers)
- [提交号码的所有权文件](/docs/guides/numbers/ownership-paperwork)
- [接收来电](/docs/guides/voice/receiving-calls)
- [SIP 中继](/docs/guides/voice/sip-trunks)
- [幂等性](/docs/guides/idempotency)

## Related resources

- [What is a virtual phone number (VMN)?](/explained/numbers/what-is-a-virtual-phone-number) (answer)
- [SMS numbers](/sms-api/features/numbers) (product)
- [Number types](/docs/guides/numbers/number-types) (docs)

[Get an implementation brief](/learn/workspace?topic=phone-numbers)
