curl --request POST \
--url https://api.surfais.com/v1/orgs/{orgId}/properties \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"domain": "<string>",
"geography": "<string>",
"external_ref": "<string>",
"competitors": [
{
"name": "<string>",
"domain": "<string>"
}
],
"markets": [
"<string>"
]
}
'import requests
url = "https://api.surfais.com/v1/orgs/{orgId}/properties"
payload = {
"name": "<string>",
"domain": "<string>",
"geography": "<string>",
"external_ref": "<string>",
"competitors": [
{
"name": "<string>",
"domain": "<string>"
}
],
"markets": ["<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({
name: '<string>',
domain: '<string>',
geography: '<string>',
external_ref: '<string>',
competitors: [{name: '<string>', domain: '<string>'}],
markets: ['<string>']
})
};
fetch('https://api.surfais.com/v1/orgs/{orgId}/properties', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
},
"competitors": [
{
"name": "<string>",
"domain": "<string>",
"status": "applied",
"competitor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reused": true,
"error": {
"code": "<string>",
"message": "<string>"
}
}
]
}
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
},
"competitors": [
{
"name": "<string>",
"domain": "<string>",
"status": "applied",
"competitor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reused": true,
"error": {
"code": "<string>",
"message": "<string>"
}
}
]
}
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}Provision a property
Provision an own brand with competitors.
Creates the own brand (is_own: true, external_ref), then links each competitor in turn — an existing competitor row in the organisation whose stored domain normalises to the same host is reused (its stored name kept), otherwise one is created. markets are validated against the market list and the organisation’s country-cap headroom and echoed back; they are NOT persisted (countries attach to prompts). Checks run before any write: 409 brand_exists (an own brand whose stored domain normalises to the same host — a dashboard-created https://www.Example.com/ matches example.com) / external_ref_exists (both with details.existing_id) and 422 country_cap_exceeded leave nothing behind, as does 422 brand_cap_exceeded. NOT ATOMIC past the brand insert: a competitor refused by the per-brand cap stops the loop and the response is 422 competitor_cap_exceeded whose details (PropertyPartialFailure) carry the CREATED brand and a per-competitor result — retry the failed ones through POST …/brands/{id}/competitors; do not re-provision. Each competitor is itself ONE atomic call, so a refused competitor leaves nothing behind — no row and no link.
curl --request POST \
--url https://api.surfais.com/v1/orgs/{orgId}/properties \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"domain": "<string>",
"geography": "<string>",
"external_ref": "<string>",
"competitors": [
{
"name": "<string>",
"domain": "<string>"
}
],
"markets": [
"<string>"
]
}
'import requests
url = "https://api.surfais.com/v1/orgs/{orgId}/properties"
payload = {
"name": "<string>",
"domain": "<string>",
"geography": "<string>",
"external_ref": "<string>",
"competitors": [
{
"name": "<string>",
"domain": "<string>"
}
],
"markets": ["<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({
name: '<string>',
domain: '<string>',
geography: '<string>',
external_ref: '<string>',
competitors: [{name: '<string>', domain: '<string>'}],
markets: ['<string>']
})
};
fetch('https://api.surfais.com/v1/orgs/{orgId}/properties', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
},
"competitors": [
{
"name": "<string>",
"domain": "<string>",
"status": "applied",
"competitor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reused": true,
"error": {
"code": "<string>",
"message": "<string>"
}
}
]
}
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {
"brand_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"brand": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>",
"is_own": true,
"external_ref": "<string>",
"geography": "<string>",
"sunset_at": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"competitors": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"domain": "<string>"
}
],
"last_scan_at": "2023-11-07T05:31:56Z",
"last_scan_changed_at": "2023-11-07T05:31:56Z",
"next_scheduled_scan": "2023-12-25",
"markets": {
"accepted": [
"<string>"
],
"currently_used": [
"<string>"
],
"cap": 0
}
},
"competitors": [
{
"name": "<string>",
"domain": "<string>",
"status": "applied",
"competitor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reused": true,
"error": {
"code": "<string>",
"message": "<string>"
}
}
]
}
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
},
"request_id": "<string>"
}Authorizations
A Surfais API key — an organisation key or a partner key. Issued by Surfais; shown once. Rotate by creating a new key, then revoking the old one.
Headers
Optional. 1–255 characters, unique per intended write. Same key + same request → the stored response is replayed with Idempotent-Replayed: true; same key + different request → 409 idempotency_key_reuse; still running → 409 idempotency_key_in_flight. Outside that range → 400 validation_error (invalid_header).
1 - 255Path Parameters
Organisation id. Org keys: the key's own org. Partner keys: any org with an active link. Anything else is 404.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Body
1 - 200Registrable host name. Normalised before storage and comparison: lower-cased, scheme / www. / path / port removed (example.com, https://www.Example.com/x and EXAMPLE.COM are the same domain).
1 - 2048Primary-market hint (alpha-2 when set by the UI). Not validated against the market list.
1 - 100Your own identifier. Unique per organisation, case-insensitively.
1 - 255Competitors to track for the property. An existing competitor row in the organisation with the same domain is reused (its stored name is kept); otherwise one is created. Duplicate domains within the request are collapsed (first wins). A competitor with the property's own domain is 400 validation_error (competitor_is_self).
100Show child attributes
Show child attributes
Markets you intend to run prompts in. Validated against the market list AND the organisation's country-cap headroom (422 country_cap_exceeded before anything is created); NOT persisted here — countries attach to prompts (POST …/prompts, PUT …/brands/{id}/prompts). Echoed in markets.
29ISO 3166-1 alpha-2 market (case-insensitive). Must be one Surfais scans; otherwise 400 validation_error with details[].code = "unsupported_country".
^[A-Za-z]{2}$Response
The created property (the brand detail) plus the markets echo.
Show child attributes
Show child attributes