You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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_emailrequired 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
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
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.
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
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.
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), whichcarries the authority of the person who created it rather than the authority of a project. Everything under
/api/spaceis 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/projectscan now create and delete root (parent) projects when the caller authenticates with a PAT. A projectAPI token remains limited to subprojects.
/api/project/tokenscan now create, update and delete API tokens for a project named explicitly in the request whenthe 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/projectsand/api/project/tokenseither credential is accepted on the same path and the prefix decides which rules apply.Authentication and scope
password (
Authorization: Basic base64(":pat_...")).guest memberships are rejected. There are no token scopes on a PAT; the holder's space role is the whole authorization
decision.
https://<your-space>.signalwire.comScoping rules a docs reader needs:
PAT used against a different subdomain resolves no membership and is rejected.
401with
{"message": "Please validate a phone number to access your account."}on every endpoint in this namespace./api/projectsand/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)GET/api/spacePUT/api/spaceGET/api/space/geographic_permissionsPUT/api/space/geographic_permissionsGET/api/space/membersGET/api/space/members/:idPOST/api/space/membersPATCHorPUT/api/space/members/:idDELETE/api/space/members/:idGET/api/space/members/:member_id/projectsPUT/api/space/members/:member_id/projects/:project_idDELETE/api/space/members/:member_id/projects/:project_idGET/api/space/balancePOST/api/space/balance/top_upsGET/api/space/low_balance_settingPUT/api/space/low_balance_settingGET/api/space/billing_profilePUT/api/space/billing_profileGET/api/space/billing_statementsGET/api/space/billing_statement?month=YYYY-MMGET/api/space/usageGET/api/space/payment_historyGET/api/space/payment_methodsDELETE/api/space/payment_methods/:idChanged behavior on existing endpoints
POST/api/projectsparent_project_idis omitted, or a subproject of the givenparent_project_id. With a project API token, behavior is unchanged (subprojects of the token's project only).DELETE/api/projects/:idGET/api/projects,/api/projects/:idPUT/api/projects/:id,POST /api/projects/:id/signing-key/rotatePOST/api/project/tokensproject_idis required in the body and names the project the token is created for. With a project API token,project_idis ignored and the token's own project is used, as before.PUT,DELETE/api/project/tokens/:idLimitations
PAT receives
401.**
billing_profile,billing_statements,billing_statement,usage,payment_history,payment_methodsand thepayment method delete require that billing is managed in the space itself, which is the case for every ordinary space.
They return
401for a space whose billing lives elsewhere, which today means a space purchased through a cloudmarketplace 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.
PUT /api/space/geographic_permissionsadditionallyrequires that the space is allowed to edit international settings; otherwise
401. A country list that maps to nosupported country is rejected with
no_supported_countries.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.POST /api/space/memberssends an invitation. The member exists immediatelywith
activated: falseand activates by accepting the invitation.adminandemployeeonly.ownercannot be assigned, and the owner's role cannot bechanged (
cannot_change_the_owners_role). The owner cannot be removed (cannot_remove_the_owner), and a callercannot remove their own membership (
cannot_remove_yourself).enabling a subproject directly is rejected with
subproject_access_is_inherited_from_root, which names the parent toenable instead. Both enable and disable are idempotent: repeating them leaves the same single enablement or absence of
one.
Idempotency-Keyrequest header. Without it the request is400. Reusing a key with the samebody replays the original top-up and returns
200instead of201. Reusing a key with a different body is400. Atop-up already in flight for that key is
409.of 10,000 microdollars.
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_idmust name a payment method on the space.with
send_topup_cannot_be_disabled.monthly_invoices: falseis rejectedwith
monthly_invoices_locked.tax_id_numberis required on the billing profile for Enterprise spaces and optional otherwise. The responseexposes
tax_id_number_requiredso a client does not have to infer it.YYYY-MM. A month before the spaceexisted is rejected (
month_precedes_space_creation), as is a future month (month_is_in_the_future). The currentmonth is available as JSON and CSV but not as PDF (
pdf_requires_a_closed_month);formatson each listed periodsays which are available.
GET /api/space/billing_statement.pdfresponds with a redirect to a storage URLrather than the PDF bytes, so a client must follow redirects.
unavailable the response is
502. The same is true of balance and geographic permissions, which read from the billingservice.
first (
subprojects_must_be_removed), phone numbers must be removed first (phone_numbers_must_be_removed), and thelast remaining project in a space cannot be deleted (
last_project_cannot_be_deleted).members, memberprojects,payment_history,and
projects).page_sizedefaults to 50 and is capped at 1000. Apage_tokenis required for any page after thefirst and is supplied by the
links.nextURL in the previous response. Filters are preserved in the pagination links.Fields
Space object
typespaceidnamesubdomainverifiedtrialcreated_atupdated_atWritable (
PUT /api/space):name(string, required, must not be blank).Geographic permission object
typegeographic_permissioncountriessupported_countriesWritable (
PUT):countries(array of string, required). The array replaces the current selection; codesoutside
supported_countriesare dropped, and if nothing supported remains the request is rejected.Member object
typememberidemailnameroleowner,admin, oremployeejob_titleentrepreneur,product_manager,developerorotheractivatedlast_logged_increated_atupdated_atWritable on create (
POST /api/space/members):email(string, required, must be a valid address not already amember of the space),
role(string, required,adminoremployee),name(string, optional).Writable on update (
PATCH /api/space/members/:id):name(string, required),role(string,optional,
adminoremployee),job_title(string, optional, one of the four values above).Query parameters on list:
role(one ofowner,admin,employee),page_size,page_number,page_token.Project object (member enablement)
typeprojectidnamecreated_atupdated_atsubprojectsBalance object
typebalancebalance_in_microdollarscurrent_balancelow_balance_threshold_in_microdollarslow_balance_thresholdauto_topup_enabledBalance adjustment object (top-up response and payment history)
typebalance_adjustmentidkindbalance_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_microdollarsamountcreated_atpayment_method_last4Query parameters on payment history:
created_after,created_before(date ortimestamp),
page_size,page_number,page_token.Low balance setting object
typelow_balance_settingthreshold_in_microdollarsthresholdsend_emailsend_webhookwebhook_urlwebhook_methodGETorPOSTsend_topuptopup_amount_in_microdollarstopup_amounttopup_payment_method_idWritable (
PUT):threshold_in_microdollars(integer, defaults to the platform default whenomitted),
send_email(boolean, defaulttrue),send_webhook(boolean, defaultfalse),webhook_url(string,required when
send_webhookis true),webhook_method(string,GETorPOST, defaultPOST),send_topup(boolean, default
false),topup_amount_in_microdollars(integer, required whensend_topupistrue),
topup_payment_method_id(UUID, required whensend_topupis true).Billing profile object
typebilling_profileaddress_line1address_line2address_cityaddress_stateaddress_zipaddress_countrycompany_namecontact_namecontact_emailcontact_phonetax_id_numbermonthly_invoicestax_id_number_requiredmonthly_invoices_lockedmonthly_invoicescannot be turned off**Writable (
PUT):**
address_line1,address_city,address_state,address_zip,address_country,company_name,contact_name,contact_phoneall required;
contact_emailrequired and must be an array with at least one valid address;address_line2optional;
tax_id_numberrequired on Enterprise spaces;monthly_invoicesboolean, defaultfalse. A never-configuredprofile is returned with null fields rather than a 404, and the update writes the whole profile.
Billing statement period object (list)
typebilling_statement_periodmonthYYYY-MMuriformats["json", "csv", "pdf"]for a closed month,["json", "csv"]for the current monthBilling statement object
typebilling_statementmonthYYYY-MMsummary.usage_in_microdollars/summary.usage_in_dollarssummary.carrier_fees_in_microdollars/summary.carrier_fees_in_dollarssummary.taxes_in_microdollars/summary.taxes_in_dollarssummary.adjustmentskind,description,amount_in_microdollars,amount_in_dollarsusage_by_kindkind,description,amount_in_microdollars,amount_in_dollarsprojectsid,name,amount_in_microdollars,amount_in_dollars, and its ownusage_by_kindQuery parameters:
month(YYYY-MM, required),kind(array, optional, filtersusage_by_kindand theper-project breakdown),
project(array of project UUID, optional).Usage object
typeusagemonthYYYY-MM, defaults to the current monthtotal_in_microdollars/total_in_dollarsusage_by_kindprojectsQuery parameters:
month(YYYY-MM, optional, defaults to the current month).Payment method object
typepayment_methodidbrandvisalast4expires_monthexpires_yearcountryauto_topup_sourceError 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:
400 Bad Request{"errors": [{"code": "not_a_valid_json", ...}]}400 Bad Request{"message": "Idempotency-Key header is required."}POST /api/space/balance/top_upssent without the header400 Bad Request{"message": "This Idempotency-Key was already used with different request parameters."}Idempotency-Keyreused with a different body401 UnauthorizedUnauthorized(plain text)401 Unauthorized{"message": "Please validate a phone number to access your account."}404 Not FoundNot Found(plain text)409 Conflict{"message": "A top-up with this Idempotency-Key is already in progress."}422 Unprocessable Entity502 Bad Gateway{"message": "Unable to deposit to the space's balance.", "code": "deposit_failed"}for a top-upNote for the docs team: authorization failures on this surface return
401, not403, and the body is the plaintext
Unauthorizedrather than a JSON error object.404is likewise plain textNot Found. That matches the rest ofthe REST API today, but it is worth stating explicitly on these pages because the role requirement makes a "wrong
role"
401common.Error codes to document
All of the following are new and need entries in the public REST error-code reference. All are
422unless noted.Space and geographic permissions
countries_must_be_arraycountrieswas sent as something other than an arrayno_supported_countriesMembers
already_a_memberinvalid_roleroleis notadminoremployeeunknown_rolerolequery filter on the member list is not a known roleinvalid_job_titlejob_titleis notentrepreneur,product_manager,developerorothercannot_change_the_owners_rolecannot_remove_the_ownercannot_remove_yourselfMember project enablement
subproject_access_is_inherited_from_rootBalance and top-ups
amount_not_a_whole_numberamount_in_microdollarsis not a whole numberamount_not_whole_centsamount_in_microdollarsis not a multiple of 10,000amount_below_minimumamount_in_microdollarsis below the 5,000,000 minimumpayment_method_invalidpayment_method_iddoes not name a payment method on this spaceLow balance and auto top-up settings
threshold_not_whole_centsthreshold_in_microdollarsis not a multiple of 10,000threshold_below_minimumthreshold_in_microdollarsis below the minimumthreshold_above_maximumthreshold_in_microdollarsis above the maximumtopup_amount_missingsend_topupis true with notopup_amount_in_microdollarstopup_amount_not_whole_centstopup_amount_in_microdollarsis not a multiple of 10,000topup_amount_below_minimumtopup_amount_in_microdollarsis below the 10,000,000 minimumtopup_payment_method_missingsend_topupis true with notopup_payment_method_idtopup_payment_method_invalidtopup_payment_method_iddoes not name a payment method on this spacesend_topup_cannot_be_disabledwebhook_url_missingsend_webhookis true with nowebhook_urlwebhook_method_invalidwebhook_methodis notGETorPOSTPayment methods
payment_method_is_active_topup_sourceBilling profile
contact_email_must_be_arraycontact_emailwas sent as something other than an arraycontact_email_missingcontact_emailis an empty arraycontact_email_address_invalidcontact_emailis not a valid email address. The message names the addressmonthly_invoices_lockedmonthly_invoicesto falseBilling statements and usage
invalid_monthmonthis not formattedYYYY-MMmonth_precedes_space_creationmonthis before the month the space was createdmonth_is_in_the_futuremonthis after the current monthpdf_requires_a_closed_monthProjects and project tokens (PAT additions)
invalid_parent_project_idparent_project_iddoes not name a project the caller can reachinvalid_project_idproject_idon a token create does not name a project the caller can reachsubprojects_must_be_removedlast_project_cannot_be_deletedChanged message on an existing code
only_subprojects_can_be_deletedRequest and response examples
Show the space -
GET /api/spaceResponse -
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/spaceRequest
{ "name": "Acme Production" }Response -
200 OKthe space object with the new name.Geographic permissions -
GETandPUT /api/space/geographic_permissionsResponse -
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=1Response -
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/membersRequest
{ "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/:idRequest
{ "name": "Jane Doe", "role": "admin", "job_title": "developer" }Response -
200 OKthe member object.Remove a member -
DELETE /api/space/members/:idResponse -
204 No Contentwith an empty body.List a member's projects -
GET /api/space/members/:member_id/projectsResponse -
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_idreturns200 OKwith the project object (includingits
subprojects).DELETEon the same path returns204 No Content. Both are idempotent.Show the balance -
GET /api/space/balanceResponse -
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_upsRequest (the
Idempotency-Keyheader is required){ "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-Keyand the same body returns200 OKwith the same object ratherthan charging again.
Response -
422 Unprocessable Entitywhen the card is declined. This is not the standard validation error body; itcarries the processor's stable decline code:
{ "message": "Your card was declined.", "decline_code": "card_declined" }Low balance settings -
GETandPUT /api/space/low_balance_settingRequest (
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 -
GETandPUT /api/space/billing_profileRequest (
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_statementsResponse -
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-07Response -
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-07returns200 OKwithContent-Type: text/csvanda
Content-Dispositionfilenameof
billing_statement_2026-07.csv.GET /api/space/billing_statement.pdf?month=2026-07responds with a redirect to thestored PDF.
Show usage -
GET /api/space/usage?month=2026-08Response -
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-01Response -
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_methodsResponse -
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/:idreturns204 No Content.Create a root project with a PAT -
POST /api/projectsRequest (omit
parent_project_idfor a root project){ "name": "New Root Project" }Response -
201 Createdthe standard project object from the existing Projects API.Create a token for a project with a PAT -
POST /api/project/tokensRequest (
project_idis required when authenticating with a PAT){ "name": "Server credential", "project_id": "4d5e6f70-8192-4a3b-8c4d-5e6f70819234", "permissions": [ "calling", "messaging" ] }Response -
200 OKthe standard token object from the existing Project Tokens API.