Management API
The Seamly Management API enables you to manage Seamly resources directly from your own application.
Use it to automate onboarding and configuration, including:
- Creating, listing, updating, and deleting organisations.
- Creating, listing, updating, and deleting projects.
- Configuring your Voice API endpoint and shared secrets for a project.
- Creating compliance addresses for an organisation.
- Listing compliance addresses whose country has telephone numbers available for automatic assignment.
- Automatically assigning telephone numbers to project voice channels and releasing assignments.
The Management API uses server-to-server authentication. You receive credentials from Seamly Support, exchange them for a short-lived access token, and use that token to authenticate your API requests.
Getting access
Contact Seamly Support to request Management API credentials.
Seamly Support will provide:
| Credential | Description |
|---|---|
client_id
|
Identifies your integration. |
client_secret
|
Secret used to authenticate your integration when requesting an access token. |
Support also configures the permissions assigned to your integration. These determine which organisations and operations you can access.
Store the credentials in your backend’s secret manager or environment configuration. Keep the client secret and access tokens out of browser code, mobile applications, source control, and logs.
Environments
| Base URL |
|---|
https://api.seamly-app.com
|
Use the credentials provided by Seamly Support for the environment.
Authentication flow
-
Request an access token using your
client_idandclient_secret. -
Read the
access_tokenfrom the response. -
Include that token in the
Authorizationheader of each Management API request. - Request a new token before the existing token expires.
1. Request an access token
Send a
POST
request to:
POST /management/token
Authenticate using
HTTP Basic authentication
, with your
client_id
as the username and your
client_secret
as the password.
Send
grant_type=client_credentials
in the request body.
Authorization: Basic <base64(client_id:client_secret)>
Content-Type: application/x-www-form-urlencoded
Do not include
client_id
or
client_secret
in the request body.
cURL example
Set these environment variables in your backend or local development environment:
export SEAMLY_BASE_URL="https://api.seamly-app.com"
export SEAMLY_CLIENT_ID="<your-client-id>"
export SEAMLY_CLIENT_SECRET="<your-client-secret>"
Request a token:
curl --fail-with-body --silent --show-error \
--request POST \
--url "${SEAMLY_BASE_URL}/management/token" \
--user "${SEAMLY_CLIENT_ID}:${SEAMLY_CLIENT_SECRET}" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials"
Successful response
The endpoint returns HTTP
200 OK
:
{
"access_token": "<access-token>",
"token_type": "Bearer",
"expires_in": 3600,
"expires_at": "2026-10-06T13:00:00Z"
}
| Property | Description |
|---|---|
access_token
|
Token used to authenticate Management API requests. |
token_type
|
Authentication scheme. The value is
Bearer
.
|
expires_in
|
Token lifetime in seconds. |
expires_at
|
Token expiration timestamp in UTC. |
Tokens currently have a lifetime of one hour. Use the expiration information in the response rather than hard-coding this duration.
Reuse the token across requests and obtain a new one shortly before it expires. To renew access, repeat the same token request using your client credentials.
2. Authenticate API requests
Include the access token in the HTTP
Authorization
header:
Authorization: Bearer <access-token>
For requests with a JSON body, also include:
Content-Type: application/json
Your token contains the permissions assigned to your integration when it was issued. If Support changes your permissions, request a new token to use the updated permissions.
Make your first API request
A useful first request is to list the organisations accessible to your integration:
GET /management/v1/organisations
Set the token returned by the authentication endpoint:
export SEAMLY_ACCESS_TOKEN="<access-token>"
Then make the request:
curl --fail-with-body --silent --show-error \
--url "${SEAMLY_BASE_URL}/management/v1/organisations" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}"
Example response
{
"organisations": [
{
"id": "985a4914-a5d7-4165-8b30-e4577444b018",
"name": "Acme"
}
]
}
Use the organisation’s
id
when making requests for its projects or compliance addresses.
The list contains organisations accessible to your integration and is ordered by name, case-insensitively.
Create a project
Once you have an organisation ID, you can create a project configured to use your Voice API implementation:
POST /management/v1/organisations/{organisation_id}/projects
A project requires:
- A name.
- The URL of your Voice API HTTP endpoint.
- One or two shared secrets, each at least 32 characters long.
These Voice API shared secrets are separate from your Management API client secret. They are used to verify the
X-Seamly-Signature
on Voice API requests.
Example request
Replace the organisation ID and example settings with your own values:
export SEAMLY_ORGANISATION_ID="985a4914-a5d7-4165-8b30-e4577444b018"
curl --fail-with-body --silent --show-error \
--request POST \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/projects" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"name": "Acme Voice",
"service_api": {
"api_endpoint": "https://api.acme.com/service",
"secrets": [
"replace-with-a-secret-of-at-least-32-characters"
]
}
}'
Example response
The endpoint returns HTTP
201 Created
:
{
"project": {
"id": "da54647a-1222-4a5e-a064-36992b0e4706",
"name": "Acme Voice",
"api_key": "34e9b8ba-64e5-4fca-8b90-2bdca1f13663",
"service_api": {
"api_endpoint": "https://api.acme.com/service",
"secrets": [
"replace-with-a-secret-of-at-least-32-characters"
]
},
"channels": [
{
"name": "voice",
"phone_numbers": []
}
]
}
}
Seamly creates the project with a generated API key and a voice channel. Creating a project does not automatically assign a telephone number. To assign one, create or select a compliance address belonging to the organisation, then use the telephone number assignment endpoint described below.
Store the returned project ID for subsequent requests. Project responses contain credentials and shared secrets, so avoid logging the complete response.
See the Voice API documentation for implementing your endpoint and verifying signed requests.
Create or select a compliance address
A telephone number assignment requires a compliance address belonging to the same organisation as the project. The address contains the organisation’s voice compliance contact and address details. Its country determines the country from which Seamly selects a telephone number.
Create a compliance address
Send a
POST
request to:
POST /management/v1/organisations/{organisation_id}/compliance_addresses
For a business, provide the contact and address details, company name, and company registration number:
curl --fail-with-body --silent --show-error \
--request POST \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/compliance_addresses" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"contact_type": "business",
"company_name": "Acme",
"contact_name": "Alex Example",
"address_line_1": "Example Street 12",
"city": "Amsterdam",
"postal_code": "1012 AB",
"country_code": "NL",
"registration_number": "00123456"
}'
Use an ISO 3166-1 alpha-2 country code, such as
NL
. For a private person, use
contact_type: "private_person"
and omit
company_name
and
registration_number
. See the OpenAPI specification for all fields and validation rules.
The endpoint returns HTTP
201 Created
:
{
"compliance_address": {
"id": "9b435e12-3973-4165-b5c4-7a36f80cdf47",
"organisation_id": "985a4914-a5d7-4165-8b30-e4577444b018",
"contact_type": "business",
"company_name": "Acme",
"contact_name": "Alex Example",
"address_line_1": "Example Street 12",
"address_line_2": null,
"city": "Amsterdam",
"region": null,
"postal_code": "1012 AB",
"country_code": "NL",
"registration_number": "00123456"
}
}
Save
compliance_address.id
for the telephone number assignment request. Creating an address does not guarantee that a telephone number is available in its country.
Select an existing compliance address
To find existing addresses whose country has telephone numbers available for automatic assignment:
curl --fail-with-body --silent --show-error \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/compliance_addresses" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}"
The endpoint returns HTTP
200 OK
with a
compliance_addresses
array, ordered by creation. Each item has the same address fields as the create response above.
This is a filtered availability list, not a complete list of the organisation’s compliance addresses. An empty array does not necessarily mean that no addresses exist:
{
"compliance_addresses": []
}
An address appearing in this list does not guarantee a successful assignment. The assignment endpoint also checks compatibility with the requested channel, and availability may change between requests.
Assign a telephone number
Once you have a project ID and a compliance address ID, send a
POST
request to:
POST /management/v1/organisations/{organisation_id}/projects/{project_id}/phone_numbers
Seamly automatically selects an eligible, unassigned telephone number from the compliance address’s country that is compatible with the requested channel. You do not choose a specific telephone number in this request.
Example request
Set the IDs returned by the earlier requests:
export SEAMLY_PROJECT_ID="da54647a-1222-4a5e-a064-36992b0e4706"
export SEAMLY_COMPLIANCE_ADDRESS_ID="9b435e12-3973-4165-b5c4-7a36f80cdf47"
Assign a number to the project’s
voice
channel:
curl --fail-with-body --silent --show-error \
--request POST \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/projects/${SEAMLY_PROJECT_ID}/phone_numbers" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--data "{
\"channel_name\": \"voice\",
\"compliance_address_id\": \"${SEAMLY_COMPLIANCE_ADDRESS_ID}\"
}"
The required fields are
channel_name
and
compliance_address_id
. The compliance address must belong to the organisation specified in the URL.
Example response
The endpoint returns HTTP
201 Created
:
{
"phone_number": {
"id": 1,
"number": "+31201234567",
"country": "NL",
"channel_name": "voice",
"compliance_address_id": "9b435e12-3973-4165-b5c4-7a36f80cdf47"
}
}
The
number
field contains the selected telephone number. Save the numeric
id
if you need to release the assignment later.
If no eligible number is available for the country and channel, the endpoint returns HTTP
409 Conflict
with
error: "no_available_phone_numbers"
. See Error handling below.
View and release assigned telephone numbers
View current assignments
Retrieve the project to see its current telephone number assignments:
curl --fail-with-body --silent --show-error \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/projects/${SEAMLY_PROJECT_ID}" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}"
The endpoint returns HTTP
200 OK
. Assignments appear in
project.channels[].phone_numbers
, with each entry containing
id
,
number
, and
compliance_address_id
.
For legacy or manually created assignments,
compliance_address_id
may be
null
.
Release an assignment
Use the numeric telephone number
id
from the assignment response or project response, not the telephone number itself:
export SEAMLY_PHONE_NUMBER_ID="1"
curl --fail-with-body --silent --show-error \
--request DELETE \
--url "${SEAMLY_BASE_URL}/management/v1/organisations/${SEAMLY_ORGANISATION_ID}/projects/${SEAMLY_PROJECT_ID}/phone_numbers/${SEAMLY_PHONE_NUMBER_ID}" \
--header "Authorization: Bearer ${SEAMLY_ACCESS_TOKEN}"
The endpoint releases the project’s telephone number assignment and returns HTTP
204 No Content
, with no response body.
Available operations
All resource endpoints use the
/management/v1
prefix.
| Resource | Operations |
|---|---|
| Organisations | List, create, retrieve, update, and delete. |
| Projects within an organisation | List, create, retrieve, update, and delete. |
| Compliance addresses within an organisation | Create; list addresses whose country has telephone numbers available for automatic assignment. |
| Telephone numbers assigned to a project | Automatically assign and release. Retrieve the project to view current assignments. |
The operations available to your integration depend on the permissions configured by Seamly Support. For example, access to an existing organisation does not automatically allow you to create new organisations.
Deleting a project is permanent. Deleting an organisation permanently deletes the organisation, its projects, and related database records.
Error handling
Token endpoint errors
Token errors use the following format:
{
"error": "invalid_client"
}
| HTTP status | Error | Meaning |
|---|---|---|
400
|
invalid_request
|
The request is malformed, for example because
grant_type
is missing or client credentials are included in the body.
|
400
|
unsupported_grant_type
|
The requested grant type is unsupported. Use
client_credentials
.
|
400
|
unauthorized_client
|
The credentials are valid, but the integration has no Management API grant. Contact Support. |
401
|
invalid_client
|
HTTP Basic credentials are missing or invalid. Check the credentials and environment. |
Management API errors
| HTTP status | Meaning | Suggested action |
|---|---|---|
400
|
Invalid JSON, where defined by the endpoint. | Check the request body’s JSON syntax. |
401
|
Missing, invalid, or expired access token, or no qualifying permissions. | Check the Bearer header. Obtain a new token if expired; contact Support if permissions are missing. |
403
|
The operation is not allowed. | Check whether your integration has permission for this operation. |
404
|
The resource does not exist or is inaccessible. | Check the resource ID, environment, and access permissions. |
409
|
No eligible telephone number is available for the compliance address’s country and requested channel. | Check the selected address and channel; contact Seamly Support if you need additional availability. |
422
|
Invalid request data or another validation failure. | Inspect the response and correct the request. |
When no eligible telephone number is available, the assignment endpoint returns:
{
"error": "no_available_phone_numbers"
}
Validation errors may include field-level details:
{
"errors": [
{
"title": "Invalid value",
"detail": "can't be blank",
"source": {
"pointer": "/name"
}
}
]
}
Handle errors according to the endpoint’s specification. Avoid automatically retrying create or delete requests after a timeout: the operation may already have completed.
If a telephone number assignment request times out, retrieve the project and check its current assignments before retrying. The number may already have been assigned, and repeating the request could assign an additional number.
OpenAPI specification
The OpenAPI specification provides the complete reference for endpoint paths, request fields, response schemas, and status codes.
Use this guide to get started and the specification as the authoritative API reference.