SSpiderIQ
SSpiderIQ

Docs / api-reference/idap

IDAP

12 endpoints from the published OpenAPI import.

GET/api/v1/idap/health

IDAP health check

Liveness probe for the IDAP router. Unauthenticated. Returns the list of supported resource_types (businesses, people, leads, ...) backed by norm_cli_* per-client tables.

Try it

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/health' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/health",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/health", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/health", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Successful Response
POST/api/v1/idap/batch

Batch-fetch resources by IDAP ref

Resolve many IdapRef entries (type + id) in a single call. Missing or unauthorized refs are returned in the errors map; successful lookups populate results. Scoped to the authenticated client's norm_cli_* tables.

Try it

Examples

cURL
curl -X POST 'https://spideriq.ai/api/v1/idap/batch' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "refs": [
    "string"
  ],
  "fields": "string",
  "include": "string"
}'
Python
import httpx

resp = httpx.post(
    "https://spideriq.ai/api/v1/idap/batch",
    headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
    json={"refs": ["string"], "fields": "string", "include": "string"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/batch", {
  method: "POST",
  headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
  body: JSON.stringify({"refs": ["string"], "fields": "string", "include": "string"})
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{"refs": ["string"], "fields": "string", "include": "string"}`)
	req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/idap/batch", body)
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/media/{media_id}

Proxy media binary from SeaweedFS

Stream a media file (image/video/document) from SeaweedFS through the API, enforcing tenant scoping. Supports conditional requests (If-None-Match, If-Modified-Since → 304), ?thumb=1 for a 400px thumbnail, and ?download=1 to force Content-Disposition: attachment.

Parameters

NameInTypeRequiredDescription
media_idpathstringtrue
thumbquerybooleanfalseReturn 400px thumbnail
downloadquerybooleanfalseSet Content-Disposition: attachment
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/media/{media_id}' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/media/{media_id}",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/media/{media_id}", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/media/{media_id}", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
304Not modified (conditional-request hit)
GET/api/v1/idap/{resource_type}/search

Full-text search within a resource type

Full-text search across the authenticated client's norm_cli_* table for the given resource_type. Use q for the query, fields for column projection, flags to filter by flag keys, and ?format=json|yaml|md|llm for AI-agent-friendly output.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
qquerystringtrueSearch query
limitqueryintegerfalse
fieldsqueryanyfalse
formatqueryanyfalseResponse format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422.
flagsqueryanyfalse
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}/search' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}/search",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/search", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}/search", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/{resource_type}/stats

Aggregate stats for a resource type

Dashboard-oriented aggregates for the authenticated client's norm_cli_{resource_type} table: total count, per-flag-key counts, recency buckets. Cheap — pre-aggregated on read. Supports ?format=json|yaml|md|llm for AI-agent-friendly output.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
formatqueryanyfalseResponse format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422.
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}/stats' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}/stats",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/stats", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}/stats", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/{resource_type}/resolve

Resolve a resource by external identifier

Look up a single resource by an external id (Google place_id, email domain, etc.) within the authenticated client's tenant scope. Returns the canonical IDAP record without requiring the internal UUID. Exactly one external identifier query param must be supplied.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
place_idqueryanyfalseGoogle Place ID (e.g. 0x47e66fdad6f1cc73:0x341211b3fccd79e1)
domainqueryanyfalseDomain name (e.g. mariospizzeria.com)
emailqueryanyfalseEmail address (e.g. info@example.com)
urlqueryanyfalseLinkedIn URL or other URL identifier (linkedin_profiles only)
linkedinqueryanyfalseLinkedIn profile URL on a contact (e.g. https://linkedin.com/in/jane)
twitterqueryanyfalseTwitter profile URL on a contact
vatqueryanyfalseVAT number (e.g. DE123456789, GB123456789)
registration_numberqueryanyfalseNational registration number (e.g. UK Companies House 00445790)
leiqueryanyfalseLegal Entity Identifier (20-char ISO 17442 code)
tax_idqueryanyfalseNational tax id (e.g. US EIN, DE Steuernummer)
source_idqueryanyfalseRegistry-provided source id (admin/dev path — composite UNIQUE with source, seq-scans)
pin_namequeryanyfalseVayaPin PIN name (e.g. 'BB:TAPAS') — pins only
pin_data_set_idqueryanyfalseVayaPin data set UUID — pins only (natural key)
account_idqueryanyfalseVayaPin account UUID — pins only
pin_subscription_idqueryanyfalseVayaPin subscription UUID — pins only
includequeryanyfalseComma-separated related types to include (e.g. 'emails,phones')
fieldsqueryanyfalseComma-separated field projection
formatqueryanyfalseResponse format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422.
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}/resolve' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}/resolve",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/resolve", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}/resolve", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/{resource_type}/duplicates

Find duplicate resources sharing a common external key

Return clusters of resources in the authenticated client's tenant that share the same value for a whitelisted external key (e.g. two or more businesses rows with the same google_place_id). Each cluster has count >= 2 — single-occurrence values are filtered out by HAVING COUNT(*) > 1. Used in dedupe workflows: surface the candidates here, then call DELETE /idap/<resource_type>/{id} (Wave D.2) to remove the duplicate. Whitelisted key values for businesses (Wave D.1): google_place_id, domain, phone_e164 (direct columns), vat, registration_number, lei, tax_id (joined via company_registry.business_id).

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
keyquerystringtrueWhitelisted clustering key. Allowed values depend on resource_type — see endpoint description. Free strings outside the whitelist return 400.
limitqueryintegerfalseMax clusters to return (1..500). Default 100.
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}/duplicates' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}/duplicates",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/duplicates", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}/duplicates", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
POST/api/v1/idap/{resource_type}/flags/bulk

Bulk add/remove flags on many resources

Apply flag writes (add/remove) to many resources of the same type in one call. Per-row failures are returned in errors; successful writes go in results. Scoped to the authenticated client's tenant tables.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
Try it
Query

Examples

cURL
curl -X POST 'https://spideriq.ai/api/v1/idap/{resource_type}/flags/bulk' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "updates": [
    {
      "resource_id": "string",
      "add": [
        "string"
      ],
      "remove": [
        "string"
      ]
    }
  ],
  "flagged_by": "string",
  "reason": "string"
}'
Python
import httpx

resp = httpx.post(
    "https://spideriq.ai/api/v1/idap/{resource_type}/flags/bulk",
    headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
    json={"updates": [{"resource_id": "string", "add": ["string"], "remove": ["string"]}], "flagged_by": "string", "reason": "string"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/flags/bulk", {
  method: "POST",
  headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
  body: JSON.stringify({"updates": [{"resource_id": "string", "add": ["string"], "remove": ["string"]}], "flagged_by": "string", "reason": "string"})
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{"updates": [{"resource_id": "string", "add": ["string"], "remove": ["string"]}], "flagged_by": "string", "reason": "string"}`)
	req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/idap/{resource_type}/flags/bulk", body)
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
POST/api/v1/idap/{resource_type}/{resource_id}/flags

Add or remove flags on a single resource

Write one or more flag changes (add/remove) against a single resource in the authenticated client's tenant scope. The response returns the resulting full flag state. Use /flags/bulk when batching across many resources of the same type.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
resource_idpathstringtrue
Try it
Query

Examples

cURL
curl -X POST 'https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}/flags' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "add": [
    "string"
  ],
  "remove": [
    "string"
  ],
  "flagged_by": "string",
  "reason": "string"
}'
Python
import httpx

resp = httpx.post(
    "https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}/flags",
    headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
    json={"add": ["string"], "remove": ["string"], "flagged_by": "string", "reason": "string"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}/flags", {
  method: "POST",
  headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
  body: JSON.stringify({"add": ["string"], "remove": ["string"], "flagged_by": "string", "reason": "string"})
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{"add": ["string"], "remove": ["string"], "flagged_by": "string", "reason": "string"}`)
	req, _ := http.NewRequest("POST", "https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}/flags", body)
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/{resource_type}/{resource_id}

Fetch a single resource by ID

Return the canonical IDAP record for a single resource in the authenticated client's tenant. Supports fields projection, include (comma-separated related-resource expansions), and ?format=json|yaml|md|llm for AI-agent-friendly output. An invalid UUID is treated as a 404 rather than a 500.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
resource_idpathstringtrue
fieldsqueryanyfalseComma-separated field projection
includequeryanyfalseComma-separated related types to include (e.g. 'emails,phones')
formatqueryanyfalseResponse format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422.
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}/{resource_id}", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
GET/api/v1/idap/{resource_type}

List resources of a given type (paged, sync-friendly)

List resources within the authenticated client's tenant, with since/until timestamp filters for incremental sync, cursor-based pagination, fields projection, include expansion, flags filtering, and ?format=json|yaml|md|llm output. Designed so external systems (OPVS board-sync, CRMs) can incrementally pull new/changed records.

Parameters

NameInTypeRequiredDescription
resource_typepathstringtrue
sincequeryanyfalseReturn resources modified after this timestamp
untilqueryanyfalseReturn resources modified before this timestamp
limitqueryintegerfalse
cursorqueryanyfalse
fieldsqueryanyfalse
includequeryanyfalseComma-separated related types to include (e.g. 'emails,phones')
formatqueryanyfalseResponse format. json (default) and llm return JSON; yaml returns text/yaml; md returns text/markdown. Any other value is a 422.
campaign_idqueryanyfalse
sourcequeryanyfalse
flagsqueryanyfalse
sortquerystringfalse
orderquerystringfalse
Try it
Query

Examples

cURL
curl -X GET 'https://spideriq.ai/api/v1/idap/{resource_type}' \
  -H 'Authorization: Bearer <token>'
Python
import httpx

resp = httpx.get(
    "https://spideriq.ai/api/v1/idap/{resource_type}",
    headers={"Authorization": "Bearer <token>"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/{resource_type}", {
  method: "GET",
  headers: { "Authorization": "Bearer <token>" }
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://spideriq.ai/api/v1/idap/{resource_type}", nil)
	req.Header.Set("Authorization", "Bearer <token>")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Request succeeded
401Authentication failed
404Resource not found in the authenticated client's tenant-scoped tables
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
DELETE/api/v1/idap/businesses/{business_id}

Delete a business record + cascade across per-tenant tables

🔴 Destructive. Hard-deletes the business row in norm_cli_<client>.businesses AND cascades to linked tables (pins, business_contacts, business_registry, company_registry, contacts, phones, domains, linkedin_profiles). Transactional — all or nothing. Audit row written to public.idap_deletions_audit in the same transaction.

Does NOT touch cs.vayapin.com. VayaPin pin pages are permanent per VayaPin §10. Response includes vayapin_pins_remain_external + the orphaned pin_data_set_ids so the caller knows what external state remains.

Does NOT cascade emails. That table is a tenant-wide canonical store keyed on email UNIQUE — multiple businesses share verification rows. The business_contacts join row is what gets removed; the email's verification metadata stays.

booking_flows + services auto-cascade via Postgres ON DELETE CASCADE FK; counts surface in auto_cascaded.

bookings is a blocker. If any bookings row references this business or its contacts, the call returns 409 with the blocking booking IDs — caller resolves those first.

Tenant-owner auth only: caller's client_id IS the tenant scope. Idempotent: second call on the same UUID returns 404.

Parameters

NameInTypeRequiredDescription
business_idpathstringtrueUUID of the business to delete. Resolve alternate keys via GET /idap/businesses/resolve first.
Try it
Query

Examples

cURL
curl -X DELETE 'https://spideriq.ai/api/v1/idap/businesses/{business_id}' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "reason": "string"
}'
Python
import httpx

resp = httpx.delete(
    "https://spideriq.ai/api/v1/idap/businesses/{business_id}",
    headers={"Authorization": "Bearer <token>", "Content-Type": "application/json"},
    json={"reason": "string"},
)
resp.raise_for_status()
print(resp.json())
JavaScript
const resp = await fetch("https://spideriq.ai/api/v1/idap/businesses/{business_id}", {
  method: "DELETE",
  headers: { "Authorization": "Bearer <token>", "Content-Type": "application/json" },
  body: JSON.stringify({"reason": "string"})
});
const data = await resp.json();
console.log(data);
Go
package main

import (
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{"reason": "string"}`)
	req, _ := http.NewRequest("DELETE", "https://spideriq.ai/api/v1/idap/businesses/{business_id}", body)
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

Responses

StatusDescription
200Business + child rows deleted; audit row written. Response body reports per-table cascade counts + the VayaPin pin IDs that remain external (cs.vayapin.com pages are permanent per VayaPin §10 — we delete OUR record only).
401Authentication failed
404Business UUID not found in the caller's tenant schema. Also returned on idempotent re-DELETE of an already-deleted ID.
422Validation error (invalid resource_type, cursor, filter, or flag payload)
429Rate limit exceeded
409One or more bookings rows reference this business or its contacts via ON DELETE NO ACTION FK. Body lists the blocking booking IDs (up to 50). Caller must re-point or delete the bookings before re-trying.