> Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header `Accept: text/markdown`. The root index at https://signalwire.com/docs/llms.txt lists the available documentation indexes.

# Projects

> Projects group the phone numbers, Resources, and credentials in your Space, and Subprojects nest one level beneath a root Project with their own Project ID and Resources.

A Project is the container for what you build in a SignalWire Space.
Phone numbers, [Resources](/docs/platform/resources), and
[API credentials](/docs/platform/your-signalwire-api-space) all belong to exactly one Project,
which is how you group work by customer, by environment, or by any other classification you like.

Your Space starts with one Project, created alongside the Space itself, and you can add more
at any time.

## What a Project scopes

Each Project carries its own Project ID and its own API tokens, and every request you make is
authenticated as one Project and acts on that Project alone.

Security settings belong to the Project too.
Each Project has its own [media URL protection](/docs/platform/media-protection) settings for
recordings, message media, and fax media, and its own choice of whether webhooks and callbacks
must use HTTPS.

Not everything divides along Project lines.
Voice and messaging [rate limits](/docs/platform/rate-limits) are account-level, counted across
every Project in your Space.

## Work with Projects in the Dashboard

The Project name at the top of the Dashboard opens the Project menu, where every Project-level
action starts:

* **Project Configuration** opens the active Project's settings, including its
  [media URL protection](/docs/platform/media-protection) toggles.
* **Switch Project** changes which Project the Dashboard shows.
* **Create New Project** adds a Project to your Space.

A Project's Project ID, Space URL, API tokens, and signing key are on its
[API Credentials](/docs/platform/your-signalwire-api-space) page, which always shows the active
Project — switch Projects to reach another one's credentials.

## Subprojects

A Subproject is a Project nested one level beneath a root Project.
It is a full Project with its own Project ID and Resources, but a Subproject cannot contain other
Subprojects.

Unlike root Projects, Subprojects can be created and deleted through the API, so an application can
open and retire a Project per customer, per tenant, or per environment on its own.
Root Projects are created in the Dashboard and can't be deleted through the API.

### Create a Subproject

Authenticate as the root Project and send the Subproject's name, along with any security settings
you want it to start with.
A Subproject cannot create another Subproject, so a request authenticated as a Subproject fails
with `422 nested_subprojects_not_allowed`.

### Request

POST https\://%7BYour\_Space\_Name%7D.signalwire.com/api/projects

```curl
curl -X POST https://{your_space_name}.signalwire.com/api/projects \
     -H "Content-Type: application/json" \
     -u "<project_id>:<api_token>" \
     -d '{
  "name": "Acme Staging"
}'
```

```python
import requests

url = "https://{your_space_name}.signalwire.com/api/projects"

payload = { "name": "Acme Staging" }
headers = {
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers, auth=("<project_id>", "<api_token>"))

print(response.json())
```

```javascript
const url = 'https://{your_space_name}.signalwire.com/api/projects';
const credentials = btoa("<project_id>:<api_token>");

const options = {
  method: 'POST',
  headers: {
    Authorization: `Basic ${credentials}`,
    'Content-Type': 'application/json'
  },
  body: '{"name":"Acme Staging"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://{your_space_name}.signalwire.com/api/projects"

	payload := strings.NewReader("{\n  \"name\": \"Acme Staging\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.SetBasicAuth("<project_id>", "<api_token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://{your_space_name}.signalwire.com/api/projects")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request.basic_auth("<project_id>", "<api_token>")
request["Content-Type"] = 'application/json'
request.body = "{\n  \"name\": \"Acme Staging\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://{your_space_name}.signalwire.com/api/projects")
  .basicAuth("<project_id>", "<api_token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"name\": \"Acme Staging\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://{your_space_name}.signalwire.com/api/projects', [
  'body' => '{
  "name": "Acme Staging"
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
    'auth' => ['<project_id>', '<api_token>'],
]);

echo $response->getBody();
```

```csharp
using RestSharp;
using RestSharp.Authenticators;

var client = new RestClient("https://{your_space_name}.signalwire.com/api/projects");
client.Authenticator = new HttpBasicAuthenticator("<project_id>", "<api_token>");
var request = new RestRequest(Method.POST);

request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"Acme Staging\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let credentials = Data("<project_id>:<api_token>".utf8).base64EncodedString()

let headers = [
  "Authorization": "Basic \(credentials)",
  "Content-Type": "application/json"
]
let parameters = ["name": "Acme Staging"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://{your_space_name}.signalwire.com/api/projects")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

SignalWire signs every request it makes to your webhooks with the Project's `signing_key`, so your
server can [verify that a request really came from SignalWire](/docs/swml/guides/webhook-security).
The create response is the only place the API returns that key, so capture it here.
Afterward you can read it from the new Project's **API Credentials** page in the Dashboard, or
replace it with
[Rotate a project's signing key](/docs/apis/rest/projects/rotate-signing-key).

### Response (201)

```json
{
  "id": "8f14e45f-ceea-467d-9c2b-7a1d3a9b2c34",
  "name": "Acme Staging",
  "parent_project_id": "b3877739-5c7e-4d4f-9d1a-2f0c8c2f1a11",
  "subproject": true,
  "region_preference": "us-west",
  "protect_recordings": false,
  "protect_message_media": false,
  "protect_fax_media": false,
  "force_https_requests": true,
  "created_at": "2024-05-06T12:20:00Z",
  "updated_at": "2024-05-06T12:20:00Z",
  "signing_key": "PSK_4d8c2b1a9f3e7c6d5b4a3e2f1d0c9b8a"
}
```

To get credentials for the new Subproject, call
[Create API token](/docs/apis/rest/project-tokens/create-token) from the root Project with the
Subproject's ID in `subproject_id`.

### Delete a Subproject

Only Subprojects can be deleted through the API; targeting the root Project returns
`422 only_subprojects_can_be_deleted`.

#### Release phone numbers first

A Project must have no phone numbers before it can be deleted, or the request returns
`422 phone_numbers_must_be_removed`.
Deleting a Subproject also migrates its registry brands and campaigns up to the parent Project.

### Request

DELETE https\://%7BYour\_Space\_Name%7D.signalwire.com/api/projects/\{id}

```curl
curl -X DELETE https://{your_space_name}.signalwire.com/api/projects/id \
     -u "<project_id>:<api_token>"
```

```python
import requests

url = "https://{your_space_name}.signalwire.com/api/projects/id"

response = requests.delete(url, auth=("<project_id>", "<api_token>"))

print(response.json())
```

```javascript
const url = 'https://{your_space_name}.signalwire.com/api/projects/id';
const credentials = btoa("<project_id>:<api_token>");

const options = {method: 'DELETE', headers: {Authorization: `Basic ${credentials}`}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://{your_space_name}.signalwire.com/api/projects/id"

	req, _ := http.NewRequest("DELETE", url, nil)

	req.SetBasicAuth("<project_id>", "<api_token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://{your_space_name}.signalwire.com/api/projects/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Delete.new(url)
request.basic_auth("<project_id>", "<api_token>")

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.delete("https://{your_space_name}.signalwire.com/api/projects/id")
  .basicAuth("<project_id>", "<api_token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', 'https://{your_space_name}.signalwire.com/api/projects/id', [
  'headers' => [
  ],
    'auth' => ['<project_id>', '<api_token>'],
]);

echo $response->getBody();
```

```csharp
using RestSharp;
using RestSharp.Authenticators;

var client = new RestClient("https://{your_space_name}.signalwire.com/api/projects/id");
client.Authenticator = new HttpBasicAuthenticator("<project_id>", "<api_token>");
var request = new RestRequest(Method.DELETE);

IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let credentials = Data("<project_id>:<api_token>".utf8).base64EncodedString()

let headers = ["Authorization": "Basic \(credentials)"]

let request = NSMutableURLRequest(url: NSURL(string: "https://{your_space_name}.signalwire.com/api/projects/id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "DELETE"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

## Manage Projects through the API

Every Projects API request reaches only the authenticated Project and the Subprojects beneath it.
[List projects](/docs/apis/rest/projects/list-projects) returns the authenticated Project
alongside its Subprojects, and each entry reports whether it is a Subproject and which Project it
belongs to.
A Project ID outside that tree returns `404 Not Found`.

[Update a project](/docs/apis/rest/projects/update-project) changes the name and security settings
of any Project in that tree, root Project included.
[Rotate a project's signing key](/docs/apis/rest/projects/rotate-signing-key) issues a new
webhook-signing key for one; the old key keeps working for a minute or two.

Integrations built around Compatibility API Account SIDs see the same tree as Accounts:
[Create Subprojects](/docs/compatibility-api/rest/accounts/create-subprojects) and
[List accounts](/docs/compatibility-api/rest/accounts/list-accounts) cover the same ground with
form-encoded requests.

## Next steps

#### [Projects API reference](/docs/apis/rest/projects/list-projects)

Every operation for listing, creating, updating, and deleting Projects.

#### [API credentials](/docs/platform/your-signalwire-api-space)

Find a Project's Project ID and Space URL, and issue API tokens.