Plan data source filters
curl --request POST \
--url https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Team-SID: <api-key>' \
--data '
{
"prompt": "<string>",
"company_scoped": true
}
'import requests
url = "https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan"
payload = {
"prompt": "<string>",
"company_scoped": True
}
headers = {
"Authorization": "Bearer <token>",
"Team-SID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'Team-SID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({prompt: '<string>', company_scoped: true})
};
fetch('https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan', 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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan",
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([
'prompt' => '<string>',
'company_scoped' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Team-SID: <api-key>"
],
]);
$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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan"
payload := strings.NewReader("{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Team-SID", "<api-key>")
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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan")
.header("Authorization", "Bearer <token>")
.header("Team-SID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Team-SID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"operation": "action",
"action": "<string>",
"item": "<unknown>",
"result": {
"engine": "<string>",
"type": "ai_search",
"object_type": "people",
"filters": {},
"chips": [
{
"key": "<string>",
"term": "<string>",
"id": "<string>",
"label": "<string>"
}
],
"unresolved": [
{
"key": "<string>",
"term": "<string>"
}
],
"warnings": [
"<string>"
],
"suggested_name": "<string>"
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}data_sources
Plan data source filters
Turn a description of who to find into a wire-ready filter set without creating anything: the step before create_data_source when the user should see or edit what will be searched. Paid: the AI parse costs one credit per call. It reads the words into facets (location, industry, current and past company, school, seniority, job function, company headcount, titles as free text) and the platform pool resolves every id-bearing term; a term with no id lands in result.unresolved, never faked. Facets are ANDed and the values inside one facet ORed; there are no exclusion filters, so describe who to find, never who to leave out. A facet keeps at most 10 values: the first ten stay, the dropped terms are named in result.warnings.
Hand the answer to create_data_source as is: type and object_type from the result, name = suggested_name, payload = {filters, prompt: <the description>} (find_people adds payload.company_sids). create_data_source with payload.prompt alone plans the same way at its first collect step, so call this only when the plan must be seen first.
Refusals: 422 on prompt when nothing usable was read (ai_parse_failed or ai_empty_filter: rephrase with roles, places or industries); a busy pool answers 503 service_unavailable (context.reason infrastructure_unavailable or search_in_flight) or 429 rate_limited (pool_rate_limited), each with context.retry_after: wait until then, or create the source with the prompt alone and let its collector plan at the pool's pace.
Contract:
- MCP tool `plan_data_source_filters`, registry package `mcp.playtables/data_sources`, mount `playtables.sources`.
- Operation `action`, response envelope `action`.
- Flags: none.
POST
/
api
/
data-sources
/
ai-plan
Plan data source filters
curl --request POST \
--url https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Team-SID: <api-key>' \
--data '
{
"prompt": "<string>",
"company_scoped": true
}
'import requests
url = "https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan"
payload = {
"prompt": "<string>",
"company_scoped": True
}
headers = {
"Authorization": "Bearer <token>",
"Team-SID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'Team-SID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({prompt: '<string>', company_scoped: true})
};
fetch('https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan', 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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan",
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([
'prompt' => '<string>',
'company_scoped' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Team-SID: <api-key>"
],
]);
$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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan"
payload := strings.NewReader("{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Team-SID", "<api-key>")
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://beta.getsales.io/playtables/v1/api/data-sources/ai-plan")
.header("Authorization", "Bearer <token>")
.header("Team-SID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://beta.getsales.io/playtables/v1/api/data-sources/ai-plan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Team-SID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"prompt\": \"<string>\",\n \"company_scoped\": true\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"operation": "action",
"action": "<string>",
"item": "<unknown>",
"result": {
"engine": "<string>",
"type": "ai_search",
"object_type": "people",
"filters": {},
"chips": [
{
"key": "<string>",
"term": "<string>",
"id": "<string>",
"label": "<string>"
}
],
"unresolved": [
{
"key": "<string>",
"term": "<string>"
}
],
"warnings": [
"<string>"
],
"suggested_name": "<string>"
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}{
"success": false,
"error": {
"code": "validation_failed",
"message": "<string>",
"recoverable": true,
"suggestion": "<string>",
"field_errors": {},
"blockers": [
{
"type": "<string>",
"severity": "hard",
"description": "<string>",
"entity_sid": "<string>",
"resolution": "<string>",
"resolution_hint": "<string>",
"count": 123
}
],
"context": {}
},
"meta": {
"trace_id": "<string>",
"span_id": "<string>",
"timestamp": "<string>",
"duration_ms": 1,
"team_sid": "<string>",
"actor_type": "<string>"
}
}Authorizations
Access token issued by gs.service.id. Its access_identity claim carries team_sid, actor_sid and actor_type, and that team scope is authoritative.
Team scope for tokens that do not carry one. Ignored when the token already names a team.
Body
application/json
Request body of plan_data_source_filters.
Who to find, in the user's words ("CMOs at fintech companies in DACH", "heads of growth").
Required string length:
3 - 2000people (default) | company - what the search brings back.
Available options:
people, company Default false. true = a find_people plan: who to find inside companies selected in a grid; current companies are left out of the filter, the selection supplies them.
Response
action success envelope.