Skip to content

[New Endpoints] Space Administration API (api/space) and Personal Access Token support on Projects and Project Tokens #624

Description

@cassieemb

references https://github.com/signalwire/cloud-product/issues/19429

Tip

Prefer to start from the spec? A complete OpenAPI 3.0.3 document for every endpoint in this release is in the comments, ready to drop into a client generator or an API client.

Summary

The Space Administration API adds a customer-facing REST surface for the things that previously could only be done by a
human in the SignalWire dashboard: renaming a space, setting its geographic permissions, managing members and the
projects each member can reach, reading the balance and topping it up, managing low balance and auto top-up settings,
reading and updating the billing profile, listing and downloading monthly billing statements, reading company-wide
usage, reading payment history, and listing or deleting payment methods.

It is a new namespace, /api/space, and a new credential for the REST API: the Personal Access Token (PAT), which
carries the authority of the person who created it rather than the authority of a project. Everything under /api/space
is space-wide and therefore has no project in its path.

The same epic also taught two existing endpoint families to accept a PAT, which unlocks operations a project API token
cannot perform:

  • /api/projects can now create and delete root (parent) projects when the caller authenticates with a PAT. A project
    API token remains limited to subprojects.
  • /api/project/tokens can now create, update and delete API tokens for a project named explicitly in the request when
    the caller authenticates with a PAT, so a newly created root project can be given its first credential over the API.

This is live in production as of August 20th.

Note

The credential is a Personal Access Token, not a project API token. PATs are created by a user in the dashboard (Personal Access Tokens page) and are prefixed pat_.

There is no public API for creating a PAT. A request is recognized as PAT-authenticated by that prefix, so on /api/projects and /api/project/tokens either credential is accepted on the same path and the prefix decides which rules apply.

Authentication and scope

  • Auth: HTTP Basic auth. Leave the username empty and send the Personal Access Token as the
    password (Authorization: Basic base64(":pat_...")).
  • Required role: the PAT holder must be an owner or admin of the space named by the subdomain. Employee and
    guest memberships are rejected. There are no token scopes on a PAT; the holder's space role is the whole authorization
    decision.
  • Base URL: https://<your-space>.signalwire.com

Scoping rules a docs reader needs:

  • The space is resolved from the subdomain, so a PAT only ever acts on the space whose subdomain was called. The same
    PAT used against a different subdomain resolves no membership and is rejected.
  • The space must be verified. An unverified space receives 401
    with {"message": "Please validate a phone number to access your account."} on every endpoint in this namespace.
  • The billing endpoints additionally require that billing is managed in the space itself. See Limitations.
  • On /api/projects and /api/project/tokens, a PAT reaches only the projects that its holder has been enabled for,
    plus the subprojects of those projects. A project id outside that set returns 404.

Endpoints

Space Administration API (/api/space)

Method Path Description
GET /api/space Show the space
PUT /api/space Update the space name
GET /api/space/geographic_permissions List the selected and supported countries
PUT /api/space/geographic_permissions Replace the selected countries
GET /api/space/members List members, optionally filtered by role
GET /api/space/members/:id Show a member
POST /api/space/members Invite a member
PATCH or PUT /api/space/members/:id Update a member's name, role or job title
DELETE /api/space/members/:id Remove a member from the space
GET /api/space/members/:member_id/projects List the projects a member is enabled for
PUT /api/space/members/:member_id/projects/:project_id Enable a member for a project (idempotent)
DELETE /api/space/members/:member_id/projects/:project_id Disable a member for a project (idempotent)
GET /api/space/balance Show the current balance and auto top-up state
POST /api/space/balance/top_ups Charge a payment method and deposit to the balance
GET /api/space/low_balance_setting Show low balance notification and auto top-up settings
PUT /api/space/low_balance_setting Update low balance notification and auto top-up settings
GET /api/space/billing_profile Show the billing profile
PUT /api/space/billing_profile Update the billing profile
GET /api/space/billing_statements List the months a statement is available for
GET /api/space/billing_statement?month=YYYY-MM Show one month's statement as JSON, CSV or PDF
GET /api/space/usage Show company-wide usage for a month
GET /api/space/payment_history List balance adjustments (payments, credits, debits)
GET /api/space/payment_methods List the space's payment methods
DELETE /api/space/payment_methods/:id Delete a payment method

Changed behavior on existing endpoints

Method Path What changed
POST /api/projects With a PAT, creates a root project when parent_project_id is omitted, or a subproject of the given parent_project_id. With a project API token, behavior is unchanged (subprojects of the token's project only).
DELETE /api/projects/:id With a PAT, a root project may be deleted. With a project API token, only subprojects may be deleted (this is the error message change noted under Error codes).
GET /api/projects, /api/projects/:id Accepts a PAT. The listing is scoped to the projects the PAT holder is enabled for, plus their subprojects.
PUT /api/projects/:id, POST /api/projects/:id/signing-key/rotate Accepts a PAT, subject to the same scoping.
POST /api/project/tokens With a PAT, project_id is required in the body and names the project the token is created for. With a project API token, project_id is ignored and the token's own project is used, as before.
PUT, DELETE /api/project/tokens/:id Accept a PAT, subject to the same scoping.

Limitations

  • Owner and admin only. Every endpoint in this release requires an owner or admin membership. An employee or guest
    PAT receives 401.
  • **Spaces that do not manage their own billing cannot use the billing endpoints.
    ** billing_profile, billing_statements, billing_statement, usage, payment_history, payment_methods and the
    payment method delete require that billing is managed in the space itself, which is the case for every ordinary space.
    They return 401 for a space whose billing lives elsewhere, which today means a space purchased through a cloud
    marketplace such as Google Cloud Marketplace, and for a space that has been suspended or deactivated. A space
    deactivated for nonpayment keeps them, so that an outstanding balance can still be settled. The plain-language test
    for a docs reader: if the Billing section appears in your dashboard, these endpoints are available to you. Balance,
    top-up and low balance settings are not restricted this way.
  • Geographic permissions can be read but not always written. PUT /api/space/geographic_permissions additionally
    requires that the space is allowed to edit international settings; otherwise 401. A country list that maps to no
    supported country is rejected with no_supported_countries.
  • Adding a payment method is not part of this API. Cards are added in the dashboard. The API can list and delete
    them only. The one card that is the active auto top-up source cannot be
    deleted (payment_method_is_active_topup_source); change or disable auto top-up first.
  • Members are invited, not created. POST /api/space/members sends an invitation. The member exists immediately
    with activated: false and activates by accepting the invitation.
  • Assignable roles are admin and employee only. owner cannot be assigned, and the owner's role cannot be
    changed (cannot_change_the_owners_role). The owner cannot be removed (cannot_remove_the_owner), and a caller
    cannot remove their own membership (cannot_remove_yourself).
  • Project enablement applies to root projects only. Access to a root project includes all of its subprojects, so
    enabling a subproject directly is rejected with subproject_access_is_inherited_from_root, which names the parent to
    enable instead. Both enable and disable are idempotent: repeating them leaves the same single enablement or absence of
    one.
  • Top-ups require an Idempotency-Key request header. Without it the request is 400. Reusing a key with the same
    body replays the original top-up and returns 200 instead of 201. Reusing a key with a different body is 400. A
    top-up already in flight for that key is 409.
  • Top-up minimum is 5,000,000 microdollars (5.00 USD) and the amount must be a whole number of cents, so a multiple
    of 10,000 microdollars.
  • Low balance threshold must be a whole number of cents, at least 5,000,000 microdollars and at most
    1,000,000,000,000 microdollars. When auto top-up is on, the top-up amount must be set, a whole number of cents, and at
    least 10,000,000 microdollars, and topup_payment_method_id must name a payment method on the space.
  • Auto top-up cannot always be turned off. Some spaces are required to keep it on; disabling it there is rejected
    with send_topup_cannot_be_disabled.
  • Monthly invoices cannot always be turned off. On a postpay space, monthly_invoices: false is rejected
    with monthly_invoices_locked.
  • tax_id_number is required on the billing profile for Enterprise spaces and optional otherwise. The response
    exposes tax_id_number_required so a client does not have to infer it.
  • Statements are identified by calendar month, not by an id. The identifier is YYYY-MM. A month before the space
    existed is rejected (month_precedes_space_creation), as is a future month (month_is_in_the_future). The current
    month is available as JSON and CSV but not as PDF (pdf_requires_a_closed_month); formats on each listed period
    says which are available.
  • PDF is served as a redirect. GET /api/space/billing_statement.pdf responds with a redirect to a storage URL
    rather than the PDF bytes, so a client must follow redirects.
  • Statements and usage are computed on request. Both read from the usage reporting pipeline, and if it is
    unavailable the response is 502. The same is true of balance and geographic permissions, which read from the billing
    service.
  • Deleting a project through the API has preconditions. All subprojects must be removed
    first (subprojects_must_be_removed), phone numbers must be removed first (phone_numbers_must_be_removed), and the
    last remaining project in a space cannot be deleted (last_project_cannot_be_deleted).
  • Pagination is cursor-based on the list endpoints (members, member projects, payment_history,
    and projects). page_size defaults to 50 and is capped at 1000. A page_token is required for any page after the
    first and is supplied by the links.next URL in the previous response. Filters are preserved in the pagination links.

Fields

Space object

Field Type Notes
type string Always space
id string (UUID)
name string
subdomain string
verified boolean Whether the space has completed verification.
trial boolean Whether the space is on trial
created_at string (ISO 8601)
updated_at string (ISO 8601)

Writable (PUT /api/space): name (string, required, must not be blank).

Geographic permission object

Field Type Notes
type string Always geographic_permission
countries array of string Selected ISO 3166-1 alpha-2 country codes
supported_countries array of string The codes that may be selected

Writable (PUT): countries (array of string, required). The array replaces the current selection; codes
outside supported_countries are dropped, and if nothing supported remains the request is rejected.

Member object

Field Type Notes
type string Always member
id string (UUID) The membership id, used in the path
email string
name string
role string owner, admin, or employee
job_title string or null entrepreneur, product_manager, developer or other
activated boolean False until the invitation is accepted
last_logged_in string (ISO 8601) or null
created_at string (ISO 8601)
updated_at string (ISO 8601)

Writable on create (POST /api/space/members): email (string, required, must be a valid address not already a
member of the space), role (string, required, admin or employee), name (string, optional).

Writable on update (PATCH /api/space/members/:id): name (string, required), role (string,
optional, admin or employee), job_title (string, optional, one of the four values above).

Query parameters on list: role (one of owner, admin, employee), page_size, page_number, page_token.

Project object (member enablement)

Field Type Notes
type string Always project
id string (UUID)
name string
created_at string (ISO 8601)
updated_at string (ISO 8601)
subprojects array of project Present on the enablement list only; the subprojects reached through this enablement. Not present on nested entries.

Balance object

Field Type Notes
type string Always balance
balance_in_microdollars integer
current_balance number The same value in dollars
low_balance_threshold_in_microdollars integer
low_balance_threshold number The same value in dollars
auto_topup_enabled boolean

Balance adjustment object (top-up response and payment history)

Field Type Notes
type string Always balance_adjustment
id string (UUID)
kind string Adjustment kind, for example balance_top_up, auto_balance_top_up, balance_credit_by_signalwire, balance_debit_by_signalwire, coupon_code_credit, sign_up_free_credit. See the note under Open questions about publishing the full list.
amount_in_microdollars integer Signed. Credits and debits carry the sign they were recorded with.
amount number The same value in dollars
created_at string (ISO 8601)
payment_method_last4 string or null Present when the adjustment was charged to a card

Query parameters on payment history: created_after, created_before (date or
timestamp), page_size, page_number, page_token.

Low balance setting object

Field Type Notes
type string Always low_balance_setting
threshold_in_microdollars integer Falls back to the platform default when never configured
threshold number The same value in dollars
send_email boolean
send_webhook boolean
webhook_url string or null
webhook_method string or null GET or POST
send_topup boolean Whether auto top-up is enabled
topup_amount_in_microdollars integer or null
topup_amount number or null The same value in dollars
topup_payment_method_id string (UUID) or null

Writable (PUT): threshold_in_microdollars (integer, defaults to the platform default when
omitted), send_email (boolean, default true), send_webhook (boolean, default false), webhook_url (string,
required when send_webhook is true), webhook_method (string, GET or POST, default POST), send_topup (
boolean, default false), topup_amount_in_microdollars (integer, required when send_topup is
true), topup_payment_method_id (UUID, required when send_topup is true).

Billing profile object

Field Type Notes
type string Always billing_profile
address_line1 string or null
address_line2 string or null
address_city string or null
address_state string or null
address_zip string or null
address_country string or null ISO 3166-1 alpha-2
company_name string or null
contact_name string or null
contact_email array of string or null One or more addresses
contact_phone string or null
tax_id_number string or null
monthly_invoices boolean
tax_id_number_required boolean True on Enterprise spaces
monthly_invoices_locked boolean True on postpay spaces, where monthly_invoices cannot be turned off

**Writable (PUT):
** address_line1, address_city, address_state, address_zip, address_country, company_name, contact_name, contact_phone
all required; contact_email required and must be an array with at least one valid address; address_line2
optional; tax_id_number required on Enterprise spaces; monthly_invoices boolean, default false. A never-configured
profile is returned with null fields rather than a 404, and the update writes the whole profile.

Billing statement period object (list)

Field Type Notes
type string Always billing_statement_period
month string YYYY-MM
uri string Path to the statement for that month
formats array of string ["json", "csv", "pdf"] for a closed month, ["json", "csv"] for the current month

Billing statement object

Field Type Notes
type string Always billing_statement
month string YYYY-MM
summary.usage_in_microdollars / summary.usage_in_dollars integer / number
summary.carrier_fees_in_microdollars / summary.carrier_fees_in_dollars integer / number
summary.taxes_in_microdollars / summary.taxes_in_dollars integer / number
summary.adjustments array One entry per adjustment kind in the month: kind, description, amount_in_microdollars, amount_in_dollars
usage_by_kind array Company-wide usage: kind, description, amount_in_microdollars, amount_in_dollars
projects array Per project: id, name, amount_in_microdollars, amount_in_dollars, and its own usage_by_kind

Query parameters: month (YYYY-MM, required), kind (array, optional, filters usage_by_kind and the
per-project breakdown), project (array of project UUID, optional).

Usage object

Field Type Notes
type string Always usage
month string YYYY-MM, defaults to the current month
total_in_microdollars / total_in_dollars integer / number
usage_by_kind array Same shape as on the statement
projects array Same shape as on the statement

Query parameters: month (YYYY-MM, optional, defaults to the current month).

Payment method object

Field Type Notes
type string Always payment_method
id string (UUID)
brand string For example visa
last4 string
expires_month integer
expires_year integer
country string ISO 3166-1 alpha-2
auto_topup_source boolean Whether this card is wired to auto top-up

Error responses

Validation failures use the standard structured error body already documented for the REST API:

{
  "errors": [
    {
      "type": "validation_error",
      "code": "month_precedes_space_creation",
      "message": "Month is before this space existed.",
      "attribute": "month",
      "url": "https://developer.signalwire.com/rest/overview/error-codes/#month_precedes_space_creation"
    }
  ]
}

Other statuses:

HTTP Body When it happens
400 Bad Request {"errors": [{"code": "not_a_valid_json", ...}]} The request body is not valid JSON
400 Bad Request {"message": "Idempotency-Key header is required."} POST /api/space/balance/top_ups sent without the header
400 Bad Request {"message": "This Idempotency-Key was already used with different request parameters."} An Idempotency-Key reused with a different body
401 Unauthorized Unauthorized (plain text) No PAT, an unknown or revoked PAT, a PAT whose holder is not a member of the space in the subdomain, a member who is not an owner or admin, or a billing endpoint on a space that does not manage its own billing
401 Unauthorized {"message": "Please validate a phone number to access your account."} The space is not verified. This applies to every endpoint in the namespace, not just the billing ones
404 Not Found Not Found (plain text) The member, project, payment method or token id does not exist in the caller's space or reachable set
409 Conflict {"message": "A top-up with this Idempotency-Key is already in progress."} A concurrent top-up holds the same key
422 Unprocessable Entity Validation error body, or the decline body for a top-up See the error code table below
502 Bad Gateway Empty, or {"message": "Unable to deposit to the space's balance.", "code": "deposit_failed"} for a top-up An upstream billing or usage reporting service was unavailable

Note for the docs team: authorization failures on this surface return 401, not 403, and the body is the plain
text Unauthorized rather than a JSON error object. 404 is likewise plain text Not Found. That matches the rest of
the REST API today, but it is worth stating explicitly on these pages because the role requirement makes a "wrong
role" 401 common.

Error codes to document

All of the following are new and need entries in the public REST error-code reference. All are 422 unless noted.

Space and geographic permissions

HTTP Code When it happens
422 countries_must_be_array countries was sent as something other than an array
422 no_supported_countries None of the submitted countries are supported for this space

Members

HTTP Code When it happens
422 already_a_member The invited email already belongs to a member of the space
422 invalid_role role is not admin or employee
422 unknown_role The role query filter on the member list is not a known role
422 invalid_job_title job_title is not entrepreneur, product_manager, developer or other
422 cannot_change_the_owners_role An update tried to change the space owner's role
422 cannot_remove_the_owner A delete targeted the space owner
422 cannot_remove_yourself A delete targeted the calling PAT holder's own membership

Member project enablement

HTTP Code When it happens
422 subproject_access_is_inherited_from_root The path named a subproject. The message names the parent project id to enable instead

Balance and top-ups

HTTP Code When it happens
422 amount_not_a_whole_number amount_in_microdollars is not a whole number
422 amount_not_whole_cents amount_in_microdollars is not a multiple of 10,000
422 amount_below_minimum amount_in_microdollars is below the 5,000,000 minimum
422 payment_method_invalid payment_method_id does not name a payment method on this space

Low balance and auto top-up settings

HTTP Code When it happens
422 threshold_not_whole_cents threshold_in_microdollars is not a multiple of 10,000
422 threshold_below_minimum threshold_in_microdollars is below the minimum
422 threshold_above_maximum threshold_in_microdollars is above the maximum
422 topup_amount_missing send_topup is true with no topup_amount_in_microdollars
422 topup_amount_not_whole_cents topup_amount_in_microdollars is not a multiple of 10,000
422 topup_amount_below_minimum topup_amount_in_microdollars is below the 10,000,000 minimum
422 topup_payment_method_missing send_topup is true with no topup_payment_method_id
422 topup_payment_method_invalid topup_payment_method_id does not name a payment method on this space
422 send_topup_cannot_be_disabled This space is required to keep auto top-up enabled
422 webhook_url_missing send_webhook is true with no webhook_url
422 webhook_method_invalid webhook_method is not GET or POST

Payment methods

HTTP Code When it happens
422 payment_method_is_active_topup_source The card is the active auto top-up source

Billing profile

HTTP Code When it happens
422 contact_email_must_be_array contact_email was sent as something other than an array
422 contact_email_missing contact_email is an empty array
422 contact_email_address_invalid One of the addresses in contact_email is not a valid email address. The message names the address
422 monthly_invoices_locked A postpay space tried to set monthly_invoices to false

Billing statements and usage

HTTP Code When it happens
422 invalid_month month is not formatted YYYY-MM
422 month_precedes_space_creation month is before the month the space was created
422 month_is_in_the_future month is after the current month
422 pdf_requires_a_closed_month A PDF was requested for a month that has not closed yet

Projects and project tokens (PAT additions)

HTTP Code When it happens
422 invalid_parent_project_id parent_project_id does not name a project the caller can reach
422 invalid_project_id project_id on a token create does not name a project the caller can reach
422 subprojects_must_be_removed The project still has subprojects
422 last_project_cannot_be_deleted The project is the last one in the space

Changed message on an existing code

HTTP Code Change
422 only_subprojects_can_be_deleted Was "Only subprojects can be deleted through the API." Now "Only subprojects can be deleted when authenticating with a project API token." The condition also narrowed: it is raised only for project-API-token callers, since a PAT may now delete a root project

Request and response examples

Show the space - GET /api/space

Response - 200 OK

{
  "type": "space",
  "id": "1f2e3d4c-5b6a-4789-9012-3456789abcde",
  "name": "Acme Space",
  "subdomain": "acme",
  "verified": true,
  "trial": false,
  "created_at": "2026-01-15T00:00:00Z",
  "updated_at": "2026-08-20T14:19:00Z"
}

Update the space - PUT /api/space

Request

{
  "name": "Acme Production"
}

Response - 200 OK the space object with the new name.

Geographic permissions - GET and PUT /api/space/geographic_permissions

Response - 200 OK

{
  "type": "geographic_permission",
  "countries": [
    "US",
    "CA"
  ],
  "supported_countries": [
    "US",
    "CA",
    "GB"
  ]
}

Request (PUT)

{
  "countries": [
    "US"
  ]
}

List members - GET /api/space/members?role=admin&page_size=1

Response - 200 OK

{
  "links": {
    "self": "/api/space/members?page_number=0&page_size=1&role=admin",
    "first": "/api/space/members?page_size=1&role=admin",
    "next": "/api/space/members?page_number=1&page_size=1&page_token=PA9f8e7d6c-5b4a-4321-8765-fedcba987654&role=admin"
  },
  "data": [
    {
      "type": "member",
      "id": "9f8e7d6c-5b4a-4321-8765-fedcba987654",
      "email": "jane@acme.com",
      "name": "Jane Doe",
      "role": "admin",
      "job_title": "developer",
      "activated": true,
      "last_logged_in": "2026-08-19T09:12:00Z",
      "created_at": "2026-02-01T00:00:00Z",
      "updated_at": "2026-08-19T09:12:00Z"
    }
  ]
}

Invite a member - POST /api/space/members

Request

{
  "email": "newhire@acme.com",
  "name": "New Hire",
  "role": "employee"
}

Response - 201 Created

{
  "type": "member",
  "id": "2b3c4d5e-6f70-4812-9345-6789abcdef01",
  "email": "newhire@acme.com",
  "name": "New Hire",
  "role": "employee",
  "job_title": null,
  "activated": false,
  "last_logged_in": null,
  "created_at": "2026-08-20T14:19:00Z",
  "updated_at": "2026-08-20T14:19:00Z"
}

Update a member - PATCH /api/space/members/:id

Request

{
  "name": "Jane Doe",
  "role": "admin",
  "job_title": "developer"
}

Response - 200 OK the member object.

Remove a member - DELETE /api/space/members/:id

Response - 204 No Content with an empty body.

List a member's projects - GET /api/space/members/:member_id/projects

Response - 200 OK

{
  "links": {
    "self": "/api/space/members/9f8e7d6c-5b4a-4321-8765-fedcba987654/projects?page_number=0&page_size=50",
    "first": "/api/space/members/9f8e7d6c-5b4a-4321-8765-fedcba987654/projects?page_size=50"
  },
  "data": [
    {
      "type": "project",
      "id": "4d5e6f70-8192-4a3b-8c4d-5e6f70819234",
      "name": "Root Project",
      "created_at": "2026-02-01T00:00:00Z",
      "updated_at": "2026-02-01T00:00:00Z",
      "subprojects": [
        {
          "type": "project",
          "id": "5e6f7081-9243-4b5c-8d6e-7f8091a2b3c4",
          "name": "Staging",
          "created_at": "2026-03-01T00:00:00Z",
          "updated_at": "2026-03-01T00:00:00Z"
        }
      ]
    }
  ]
}

Enable and disable a member for a project

PUT /api/space/members/:member_id/projects/:project_id returns 200 OK with the project object (including
its subprojects). DELETE on the same path returns 204 No Content. Both are idempotent.

Show the balance - GET /api/space/balance

Response - 200 OK

{
  "type": "balance",
  "balance_in_microdollars": 12000000,
  "current_balance": 12.0,
  "low_balance_threshold_in_microdollars": 5000000,
  "low_balance_threshold": 5.0,
  "auto_topup_enabled": false
}

Top up the balance - POST /api/space/balance/top_ups

Request (the Idempotency-Key header is required)

Idempotency-Key: 8f14e45f-ea6f-4a3c-9d2b-1c0f5a7b3e21
{
  "amount_in_microdollars": 25000000,
  "payment_method_id": "7a8b9c0d-1e2f-4304-8516-27384950a6b7"
}

Response - 201 Created

{
  "type": "balance_adjustment",
  "id": "6f708192-a3b4-4c5d-9e6f-708192a3b4c5",
  "kind": "balance_top_up",
  "amount_in_microdollars": 25000000,
  "amount": 25.0,
  "created_at": "2026-08-20T14:19:00Z",
  "payment_method_last4": "4242"
}

Repeating the request with the same Idempotency-Key and the same body returns 200 OK with the same object rather
than charging again.

Response - 422 Unprocessable Entity when the card is declined. This is not the standard validation error body; it
carries the processor's stable decline code:

{
  "message": "Your card was declined.",
  "decline_code": "card_declined"
}

Low balance settings - GET and PUT /api/space/low_balance_setting

Request (PUT)

{
  "threshold_in_microdollars": 6000000,
  "send_email": true,
  "send_webhook": false,
  "send_topup": true,
  "topup_amount_in_microdollars": 20000000,
  "topup_payment_method_id": "7a8b9c0d-1e2f-4304-8516-27384950a6b7"
}

Response - 200 OK

{
  "type": "low_balance_setting",
  "threshold_in_microdollars": 6000000,
  "threshold": 6.0,
  "send_email": true,
  "send_webhook": false,
  "webhook_url": null,
  "webhook_method": "POST",
  "send_topup": true,
  "topup_amount_in_microdollars": 20000000,
  "topup_amount": 20.0,
  "topup_payment_method_id": "7a8b9c0d-1e2f-4304-8516-27384950a6b7"
}

Billing profile - GET and PUT /api/space/billing_profile

Request (PUT)

{
  "address_line1": "1234 Main St",
  "address_city": "Austin",
  "address_state": "TX",
  "address_zip": "78701",
  "address_country": "US",
  "company_name": "Acme Inc",
  "contact_name": "Jane Doe",
  "contact_email": [
    "jane@acme.com",
    "billing@acme.com"
  ],
  "contact_phone": "+15125550100",
  "monthly_invoices": false
}

Response - 200 OK

{
  "type": "billing_profile",
  "address_line1": "1234 Main St",
  "address_line2": null,
  "address_city": "Austin",
  "address_state": "TX",
  "address_zip": "78701",
  "address_country": "US",
  "company_name": "Acme Inc",
  "contact_name": "Jane Doe",
  "contact_email": [
    "jane@acme.com",
    "billing@acme.com"
  ],
  "contact_phone": "+15125550100",
  "tax_id_number": null,
  "monthly_invoices": false,
  "tax_id_number_required": false,
  "monthly_invoices_locked": false
}

List statement periods - GET /api/space/billing_statements

Response - 200 OK

{
  "data": [
    {
      "type": "billing_statement_period",
      "month": "2026-08",
      "uri": "/api/space/billing_statement?month=2026-08",
      "formats": [
        "json",
        "csv"
      ]
    },
    {
      "type": "billing_statement_period",
      "month": "2026-07",
      "uri": "/api/space/billing_statement?month=2026-07",
      "formats": [
        "json",
        "csv",
        "pdf"
      ]
    }
  ]
}

Show a statement - GET /api/space/billing_statement?month=2026-07

Response - 200 OK

{
  "type": "billing_statement",
  "month": "2026-07",
  "summary": {
    "usage_in_microdollars": 4820000,
    "usage_in_dollars": 4.82,
    "carrier_fees_in_microdollars": 120000,
    "carrier_fees_in_dollars": 0.12,
    "taxes_in_microdollars": 0,
    "taxes_in_dollars": 0.0,
    "adjustments": [
      {
        "kind": "balance_credit_by_signalwire",
        "description": "Balance Credit by SignalWire",
        "amount_in_microdollars": 20000000,
        "amount_in_dollars": 20.0
      }
    ]
  },
  "usage_by_kind": [
    {
      "kind": "pstn_outbound",
      "description": "Outbound Voice Minutes",
      "amount_in_microdollars": 4820000,
      "amount_in_dollars": 4.82
    }
  ],
  "projects": [
    {
      "id": "4d5e6f70-8192-4a3b-8c4d-5e6f70819234",
      "name": "Root Project",
      "amount_in_microdollars": 4820000,
      "amount_in_dollars": 4.82,
      "usage_by_kind": [
        {
          "kind": "pstn_outbound",
          "description": "Outbound Voice Minutes",
          "amount_in_microdollars": 4820000,
          "amount_in_dollars": 4.82
        }
      ]
    }
  ]
}

GET /api/space/billing_statement.csv?month=2026-07 returns 200 OK with Content-Type: text/csv and
a Content-Disposition filename
of billing_statement_2026-07.csv. GET /api/space/billing_statement.pdf?month=2026-07 responds with a redirect to the
stored PDF.

Show usage - GET /api/space/usage?month=2026-08

Response - 200 OK

{
  "type": "usage",
  "month": "2026-08",
  "total_in_microdollars": 300,
  "total_in_dollars": 0.0003,
  "usage_by_kind": [
    {
      "kind": "pstn_outbound",
      "description": "Outbound Voice Minutes",
      "amount_in_microdollars": 300,
      "amount_in_dollars": 0.0003
    }
  ],
  "projects": [
    {
      "id": "4d5e6f70-8192-4a3b-8c4d-5e6f70819234",
      "name": "Root Project",
      "amount_in_microdollars": 300,
      "amount_in_dollars": 0.0003,
      "usage_by_kind": [
        {
          "kind": "pstn_outbound",
          "description": "Outbound Voice Minutes",
          "amount_in_microdollars": 300,
          "amount_in_dollars": 0.0003
        }
      ]
    }
  ]
}

Payment history - GET /api/space/payment_history?created_after=2026-07-01

Response - 200 OK

{
  "links": {
    "self": "/api/space/payment_history?created_after=2026-07-01&page_number=0&page_size=50",
    "first": "/api/space/payment_history?created_after=2026-07-01&page_size=50"
  },
  "data": [
    {
      "type": "balance_adjustment",
      "id": "6f708192-a3b4-4c5d-9e6f-708192a3b4c5",
      "kind": "balance_top_up",
      "amount_in_microdollars": 25000000,
      "amount": 25.0,
      "created_at": "2026-07-14T10:02:00Z",
      "payment_method_last4": "4242"
    }
  ]
}

Payment methods - GET /api/space/payment_methods

Response - 200 OK (a bare array, not a paginated envelope)

[
  {
    "type": "payment_method",
    "id": "7a8b9c0d-1e2f-4304-8516-27384950a6b7",
    "brand": "visa",
    "last4": "4242",
    "expires_month": 12,
    "expires_year": 2030,
    "country": "US",
    "auto_topup_source": false
  }
]

DELETE /api/space/payment_methods/:id returns 204 No Content.

Create a root project with a PAT - POST /api/projects

Request (omit parent_project_id for a root project)

{
  "name": "New Root Project"
}

Response - 201 Created the standard project object from the existing Projects API.

Create a token for a project with a PAT - POST /api/project/tokens

Request (project_id is required when authenticating with a PAT)

{
  "name": "Server credential",
  "project_id": "4d5e6f70-8192-4a3b-8c4d-5e6f70819234",
  "permissions": [
    "calling",
    "messaging"
  ]
}

Response - 200 OK the standard token object from the existing Project Tokens API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions