curl --request POST \
--url https://app.searchable.com/api/mcp/projects \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"workspaceId": "<string>",
"domain": "<string>",
"brandName": "<string>",
"brandSummary": "<string>",
"country": "<string>",
"competitors": [
"<string>"
],
"generatePrompts": true,
"brandNameDisambiguation": "<string>",
"city": "<string>",
"language": "<string>",
"brandAliases": [
"<string>"
],
"name": "<string>",
"description": "<string>"
}
'import requests
url = "https://app.searchable.com/api/mcp/projects"
payload = {
"workspaceId": "<string>",
"domain": "<string>",
"brandName": "<string>",
"brandSummary": "<string>",
"country": "<string>",
"competitors": ["<string>"],
"generatePrompts": True,
"brandNameDisambiguation": "<string>",
"city": "<string>",
"language": "<string>",
"brandAliases": ["<string>"],
"name": "<string>",
"description": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
workspaceId: '<string>',
domain: '<string>',
brandName: '<string>',
brandSummary: '<string>',
country: '<string>',
competitors: ['<string>'],
generatePrompts: true,
brandNameDisambiguation: '<string>',
city: '<string>',
language: '<string>',
brandAliases: ['<string>'],
name: '<string>',
description: '<string>'
})
};
fetch('https://app.searchable.com/api/mcp/projects', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.searchable.com/api/mcp/projects",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'workspaceId' => '<string>',
'domain' => '<string>',
'brandName' => '<string>',
'brandSummary' => '<string>',
'country' => '<string>',
'competitors' => [
'<string>'
],
'generatePrompts' => true,
'brandNameDisambiguation' => '<string>',
'city' => '<string>',
'language' => '<string>',
'brandAliases' => [
'<string>'
],
'name' => '<string>',
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.searchable.com/api/mcp/projects"
payload := strings.NewReader("{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.searchable.com/api/mcp/projects")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.searchable.com/api/mcp/projects")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"project": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"isActive": true,
"currentRevision": 123,
"domain": "<string>",
"description": "<string>",
"iconUrl": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
},
"success": true,
"promptGeneration": "started"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}Create a project
Create a project and set it up as the app’s Add project flow does: the project on the workspace’s plan cadence, its competitors, brand profile, default location and onboarding record, counted toward the workspace’s project quota. It requires the same details the app asks for (brand name and summary, reach, country, at least one competitor); generatePrompts: true starts the app’s prompt generation in the background, and the prompts are tracked within the prompt allowance. Requires a connection minted with the project:provision capability (not the bare write scope); a project-bound credential is refused outright, since creating a project is a workspace-level action. Supports Idempotency-Key replay; a reused key with a different body returns 409 idempotency_conflict instead of replaying the first project. A duplicate domain in the same workspace returns 409 duplicate_domain.
curl --request POST \
--url https://app.searchable.com/api/mcp/projects \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"workspaceId": "<string>",
"domain": "<string>",
"brandName": "<string>",
"brandSummary": "<string>",
"country": "<string>",
"competitors": [
"<string>"
],
"generatePrompts": true,
"brandNameDisambiguation": "<string>",
"city": "<string>",
"language": "<string>",
"brandAliases": [
"<string>"
],
"name": "<string>",
"description": "<string>"
}
'import requests
url = "https://app.searchable.com/api/mcp/projects"
payload = {
"workspaceId": "<string>",
"domain": "<string>",
"brandName": "<string>",
"brandSummary": "<string>",
"country": "<string>",
"competitors": ["<string>"],
"generatePrompts": True,
"brandNameDisambiguation": "<string>",
"city": "<string>",
"language": "<string>",
"brandAliases": ["<string>"],
"name": "<string>",
"description": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
workspaceId: '<string>',
domain: '<string>',
brandName: '<string>',
brandSummary: '<string>',
country: '<string>',
competitors: ['<string>'],
generatePrompts: true,
brandNameDisambiguation: '<string>',
city: '<string>',
language: '<string>',
brandAliases: ['<string>'],
name: '<string>',
description: '<string>'
})
};
fetch('https://app.searchable.com/api/mcp/projects', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.searchable.com/api/mcp/projects",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'workspaceId' => '<string>',
'domain' => '<string>',
'brandName' => '<string>',
'brandSummary' => '<string>',
'country' => '<string>',
'competitors' => [
'<string>'
],
'generatePrompts' => true,
'brandNameDisambiguation' => '<string>',
'city' => '<string>',
'language' => '<string>',
'brandAliases' => [
'<string>'
],
'name' => '<string>',
'description' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.searchable.com/api/mcp/projects"
payload := strings.NewReader("{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.searchable.com/api/mcp/projects")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.searchable.com/api/mcp/projects")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"workspaceId\": \"<string>\",\n \"domain\": \"<string>\",\n \"brandName\": \"<string>\",\n \"brandSummary\": \"<string>\",\n \"country\": \"<string>\",\n \"competitors\": [\n \"<string>\"\n ],\n \"generatePrompts\": true,\n \"brandNameDisambiguation\": \"<string>\",\n \"city\": \"<string>\",\n \"language\": \"<string>\",\n \"brandAliases\": [\n \"<string>\"\n ],\n \"name\": \"<string>\",\n \"description\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"project": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"isActive": true,
"currentRevision": 123,
"domain": "<string>",
"description": "<string>",
"iconUrl": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
},
"success": true,
"promptGeneration": "started"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "unauthorized",
"error": "<string>",
"message": "<string>",
"howToFix": "<string>",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"requiresUpgrade": true,
"retryable": true,
"timeout": true,
"quotaExceeded": true,
"current": 123,
"limit": 123,
"bulkLimitExceeded": true,
"requested": 123,
"maxAllowed": 123,
"details": {
"formErrors": [
"<string>"
],
"fieldErrors": {}
}
}Authorizations
A Searchable API key, created under Settings → Workspace → Integrations. Send as Authorization: Bearer sea_xxxxx. Every key carries one or more scopes (read, write, admin) and may optionally be bound to a single project — see Getting started.
Headers
Optional client-generated key (e.g. a UUID). Retrying with the same key within 24h replays the original stored response (X-Idempotent-Replay: true) instead of repeating the side effect. See Idempotency.
Body
A workspace from GET /api/mcp/workspaces whose canCreateProjects is true.
The brand's website domain, e.g. example.com
The brand's name, as it's referred to in prompts and reports
2-3 sentences: what the brand sells, who it serves, and what makes it distinct
Where the brand sells; national-global is a primary market plus international reach
global, national-global, nationwide, regional, local Where most customers are: an ISO 3166-1 alpha-2 code, e.g. GB
Direct competitors, each a website domain or {domain, name}
1 - 20 elementsStart the app's prompt generation in the background (tracked prompts, within the prompt allowance)
When the name is ambiguous, a short note on which brand this is; empty when the name is distinctive
City or area, for regional or local reach
Language for the prompts, e.g. en-GB; defaults from the country
Other names the brand goes by, counted as mentions of it
25Project name; defaults to the brand name
Response
Project created.
The project shape create_project returns — curated, not the raw internal row (no userId, settings, workspaceId, or pitch fields; see the sibling ProjectListItem, which strips workspaceId the same way).
Show child attributes
Show child attributes
started, not_requested