# Skills (/docs/skills) Skills are short playbooks that install into your AI assistant and teach it how to operate your Sillage workspace. The [MCP](/docs/mcp) and [REST API](/docs/api) give an assistant the tools; skills give it the know-how, so it turns a rough ideal customer profile into a precise persona, tracks the signals that actually matter, and makes each change to your workspace safely. They are plain Markdown, open source, and distributed through the [skills.sh](https://skills.sh) standard, so they work in any compatible assistant, including Claude Code and Cursor. ## Install [#install] You need [Node.js](https://nodejs.org) 18 or newer, which provides `npx`. Check with `node -v`; if it errors, install it from [nodejs.org](https://nodejs.org) (the official installer needs no terminal) or with [Homebrew](https://brew.sh) (`brew install node`). Then run this in your project directory: ```bash npx skills add sillage-labs/skills ``` This adds a `.skills/` directory that your assistant reads automatically. Commit it so your team and CI share the same setup. Skills act on your workspace, so connect it first: the [MCP](/docs/mcp/install) (recommended) or an `sk_live_` key for the [REST API](/docs/api). To confirm the install, ask your assistant *"What Sillage skills do you have available?"* ## What's in the pack [#whats-in-the-pack] | Skill | What it does | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **sillage-onboarding** | Turns a short interview about what you sell, who buys, and the signals that matter into a precise persona and a set of high-signal agents. The part the raw tools can't do for you: sharpening sparse answers into good targeting. | | **sillage-manage-workspace** | Sets up a new workspace and safely edits a live one, following the setup loop (persona → accounts → coverage → agents → runs) and the edit loop (read → diff → patch) so changes stay safe. | | **sillage-help** | Explains how a workspace fits together (persona, target accounts, coverage, watchlists, agents, signal runs, detections, and content) and defines every term. | | **sillage-api** | Drives the same workspace over the [REST API](/docs/api) with an `sk_live_` key, for environments where you can't install the MCP. | ## Next steps [#next-steps] * [Connect the MCP](/docs/mcp/install) — the recommended way to give skills access to your workspace. * [REST API](/docs/api) — drive the same workspace over plain HTTP with an `sk_live_` key. # Authentication (/docs/api/authentication) The API uses direct-client public API keys generated in Sillage workspace settings. These keys authorize access to the workspace linked to the key. ## Header [#header] Include the API key in the `Authorization` header: ```http Authorization: Bearer ``` ## Example request [#example-request] ```bash curl -X POST "https://api.getsillage.com/api/v2/contents/query" \ -H "Authorization: Bearer $SILLAGE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"page_size": 5}' ``` ```js const response = await fetch('https://api.getsillage.com/api/v2/contents/query', { method: 'POST', headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ page_size: 5 }), }) const result = await response.json() ``` ```js import { request } from 'node:https' const req = request( new URL('https://api.getsillage.com/api/v2/contents/query'), { method: 'POST', headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, 'Content-Type': 'application/json', }, }, (res) => { let body = '' res.on('data', (chunk) => { body += chunk }) res.on('end', () => { console.log(JSON.parse(body)) }) }, ) req.end(JSON.stringify({ page_size: 5 })) ``` ```python import os import requests response = requests.post( "https://api.getsillage.com/api/v2/contents/query", headers={"Authorization": f"Bearer {os.environ['SILLAGE_API_KEY']}"}, json={"page_size": 5}, timeout=30, ) response.raise_for_status() result = response.json() ``` ## Status codes [#status-codes] | Status | Meaning | | ------ | -------------------------------------------------------------------------------------- | | 200 | The request succeeded. | | 400 | The request parameters are invalid. | | 401 | The API key is missing, invalid, or revoked. | | 402 | Insufficient credits to complete the request. | | 403 | The key does not have access to the requested workspace resource. | | 404 | The requested resource does not exist or is not visible to the key. | | 409 | Conflict with current state of the resource. | | 422 | Unprocessable entity. The request body is structurally valid but semantically invalid. | | 429 | Too many requests. The `Retry-After` header indicates when to retry. | | 500 | An unexpected server error occurred. | ## Error model [#error-model] v2 endpoints (`/api/v2/...`) return errors as [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents. The response `Content-Type` is `application/problem+json`. Each problem document contains six fields. The `type` URI is a stable slug registered at `https://docs.getsillage.com/errors/`. When no specific type applies, `type` is `about:blank`. The `errors` extension field is present only on validation errors (400, 422) and maps each invalid field to a list of messages. ```json { "type": "https://docs.getsillage.com/errors/invalid-query", "title": "Invalid query parameters", "status": 400, "detail": "page_size must be a positive integer", "instance": "/api/v2/contents", "errors": { "page_size": ["must be a positive integer"] } } ``` The v1 endpoints (`/v1/workspace/signals`, `/v1/workspace/leads`) keep the legacy `{ "error": { status, name, message } }` envelope. All v2 endpoints use the problem+json shape above. ## Common response headers [#common-response-headers] Every response includes: | Header | Description | | --------------- | --------------------------------------- | | X-Request-Id | Request UUID for debugging and support. | | X-Response-Time | Server response time. | Rate-limited responses (`429`) include: | Header | Description | | --------------------- | ------------------------------------------------------ | | X-RateLimit-Limit | Maximum number of requests allowed in the time window. | | X-RateLimit-Remaining | Remaining requests in the current window. | When you receive a `429`, read `X-RateLimit-Remaining` and back off before retrying. ## Security [#security] Store them server-side, avoid exposing them in browsers, and rotate keys when a user leaves your organization or a key may have been copied. # API (/docs/api) The API lets client applications read content, content requests, top accounts, and account mapping state from a Sillage workspace. Most API endpoints are direct-client endpoints using workspace API keys generated from Sillage settings. Account Mapping also supports partner endpoints with partner master keys. ## Base URL [#base-url] ```text https://api.getsillage.com ``` **OpenAPI Specification** — [Swagger UI](https://api.getsillage.com/api/v1/docs) · [Raw JSON spec](https://api.getsillage.com/api/v1/docs/spec) ## Authentication [#authentication] For direct-client endpoints, send your Sillage workspace API key as a bearer token: ```http Authorization: Bearer ``` Direct-client keys start with `sk_live_`. Partner master keys start with `mk_live_` and are only used for partner routes. See [Authentication](/docs/api/authentication) for key handling and error behavior. ## References [#references]
POST Contents /api/v2/contents/query GET Content /api/v2/contents/{id} GET Content Requests /api/v2/content-requests GET Content Request /api/v2/content-requests/{id} GET Company Mappings /api/v2/company-mappings GET Company Mapping /api/v2/company-mappings/{mapping_id} POST Enrich Company Mapping /api/v2/enrich-company-mapping PUT Persona /api/v2/persona POST Add Accounts /api/v2/top-account-list/accounts GET List Accounts /api/v2/top-account-list/accounts GET Account Mapping Stage /api/v2/account-mapping/{id}/stage
## Identifiers [#identifiers] All public `/v2` identifiers are integer ids. ## Dates [#dates] Date filters use ISO 8601 timestamps. Responses return timestamps in UTC. ```text 2026-06-12T10:30:00.000Z ``` ## Pagination [#pagination] List endpoints use offset/page pagination with `page` and `page_size` query parameters. See [Pagination](/docs/api/pagination). ## Errors and response headers [#errors-and-response-headers] v2 endpoints return RFC 9457 problem documents, and every response carries request-tracing and rate-limit headers — see [Authentication](/docs/api/authentication) for the full error model, status codes, and header reference. # OpenAPI Specification (/docs/api/openapi-specification) ## Import into Postman or Insomnia [#import-into-postman-or-insomnia] Paste the following URL in Postman/Insomnia to import the full spec: ``` https://api.getsillage.com/api/v1/docs/spec ``` ## Generate an SDK [#generate-an-sdk] Use `openapi-generator-cli` to generate a client SDK in your language of choice: ```bash npx @openapitools/openapi-generator-cli generate \ -i https://api.getsillage.com/api/v1/docs/spec \ -g \ -o ./sdk ``` Replace `` with `typescript-fetch`, `python`, `java`, etc. # Pagination (/docs/api/pagination) API list endpoints use offset/page pagination. ## Query parameters [#query-parameters] | Parameter | Type | Default | Description | | ---------- | ------- | ------- | -------------------------------- | | page | integer | 1 | Page number. Must be at least 1. | | page\_size | integer | 25 | Number of items per page. | ## Response shape [#response-shape] Paginated list responses return a `data` array and a `meta.pagination` object. ```json { "data": [], "meta": { "pagination": { "page": 2, "page_size": 25, "page_count": 4, "total": 87 } } } ``` Paginated responses also include these headers: | Header | Description | | ------------- | ---------------------- | | X-Total-Count | Total number of items. | | X-Page | Current page number. | | X-Page-Size | Current page size. | | X-Page-Count | Total number of pages. | When `meta.pagination.page` equals `meta.pagination.page_count`, there are no more pages. ## Example [#example] ```bash curl "https://api.getsillage.com/api/v2/content-requests?page=2&page_size=25" \ -H "Authorization: Bearer $SILLAGE_API_KEY" ``` ```js const response = await fetch( 'https://api.getsillage.com/api/v2/content-requests?page=2&page_size=25', { headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, }, }, ) const result = await response.json() ``` ```js import { request } from 'node:https' const req = request( new URL('https://api.getsillage.com/api/v2/content-requests?page=2&page_size=25'), { headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, }, }, (res) => { let body = '' res.on('data', (chunk) => { body += chunk }) res.on('end', () => { console.log(JSON.parse(body)) }) }, ) req.end() ``` ```python import os import requests response = requests.get( "https://api.getsillage.com/api/v2/content-requests", params={ "page": "2", "page_size": "25", }, headers={"Authorization": f"Bearer {os.environ['SILLAGE_API_KEY']}"}, timeout=30, ) response.raise_for_status() result = response.json() ``` # Requests Status (/docs/api/requests-status) Returns all in-flight work in your workspace — account mapping, top-account content, and signal runs — as a single `requests` list, newest first. Each item carries a `type` field telling you what kind of work it is. Use this to check what is still being prepared before concluding that results are missing. Only pending and in-progress items are returned — failed and completed requests are excluded from the snapshot. IDs in this response are specific to your environment. A numeric `id` from production is not meaningful in staging or development. ## Get in-flight requests [#get-in-flight-requests]
GET /api/v2/requests-status
### Query parameters [#query-parameters] Both are optional — without them, one call returns up to the 500 most recent in-flight items, which covers virtually every workspace. | Parameter | Type | Description | | ---------- | ------- | --------------------------------------------------------------------------------------------------- | | page | integer | Page number (default `1`). Only needed when a previous response reported `meta.page_count` above 1. | | page\_size | integer | Items per page (default and maximum `500`). | ### Example request [#example-request] ```bash curl "https://api.getsillage.com/api/v2/requests-status" \ -H "Authorization: Bearer $SILLAGE_API_KEY" ``` ```js const response = await fetch('https://api.getsillage.com/api/v2/requests-status', { headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, }, }) const result = await response.json() ``` ```js import { request } from 'node:https' const req = request( new URL('https://api.getsillage.com/api/v2/requests-status'), { headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, }, }, (res) => { let data = '' res.on('data', (chunk) => { data += chunk }) res.on('end', () => { console.log(JSON.parse(data)) }) }, ) req.end() ``` ```python import os import requests response = requests.get( "https://api.getsillage.com/api/v2/requests-status", headers={"Authorization": f"Bearer {os.environ['SILLAGE_API_KEY']}"}, timeout=30, ) response.raise_for_status() result = response.json() ``` ### Example response [#example-response] `200 OK` ```json { "data": { "requests": [ { "type": "signal", "id": 5, "status": "in_progress", "label": "Detecting signals", "created_at": "2026-06-30T08:05:00.000Z", "updated_at": "2026-06-30T08:05:00.000Z", "scope": "company", "companies": [{ "id": 88, "name": "Acme", "domain": "acme.com" }] }, { "type": "account_mapping", "id": 12, "status": "in_progress", "label": "Mapping the account", "created_at": "2026-06-30T08:00:00.000Z", "updated_at": "2026-06-30T08:01:00.000Z", "company": { "id": 88, "name": "Acme", "domain": "acme.com" } } ] }, "meta": { "in_flight_total": 2, "page": 1, "page_size": 500, "page_count": 1 } } ``` An idle workspace returns the same structure with an empty `requests` array and `in_flight_total: 0` — never a `403`. ### Response fields [#response-fields] `data.requests` mixes all kinds of in-flight work in one list, sorted by `created_at` (newest first). Branch on each item's `type`: `"account_mapping"` and `"top_account_content"` items share one shape; `"signal"` items have another. #### Items with type `account_mapping` or `top_account_content` [#items-with-type-account_mapping-or-top_account_content] | Field | Type | Description | | -------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | string | What kind of work this item is: `"account_mapping"` or `"top_account_content"`. | | id | integer | ID of the request. Specific to your environment. | | status | string | Normalized status: `"pending"` (waiting to start) or `"in_progress"` (actively processing). Failed and completed requests are excluded from this endpoint. | | label | string | Plain-language description of what is happening, safe to show to a user (e.g. "Gathering content for your accounts"). | | created\_at | string (ISO 8601) | When this request was created. | | updated\_at | string (ISO 8601) | When this request was last updated. | | company | object \| null | The company this request is running for. `null` for workspace-wide requests. | | company.id | integer | Numeric ID of the company. Specific to your environment. | | company.name | string \| null | Company name. | | company.domain | string \| null | Company domain (e.g. `acme.com`). | #### Items with type `signal` [#items-with-type-signal] Signal runs have a different shape because one run can cover multiple accounts: | Field | Type | Description | | ------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | string | What kind of work this item is: always `"signal"` for this shape. | | id | integer | ID of the run. Specific to your environment. | | status | string | Normalized status: `"pending"` (waiting to start) or `"in_progress"` (actively processing). Failed and completed runs are excluded from this endpoint. | | label | string | Plain-language description of what is happening, safe to show to a user (e.g. "Detecting signals"). | | created\_at | string (ISO 8601) | When the run was started. | | updated\_at | string (ISO 8601) | When the run was last updated. | | scope | string | `"company"` when the run targets specific accounts; `"workspace"` when it spans the whole workspace. An empty `companies` array with `scope: "workspace"` is expected, not missing data. | | companies | array | The accounts being scanned. Empty when `scope` is `"workspace"`. | | companies\[].id | integer | Numeric ID of the company. | | companies\[].name | string \| null | Company name. | | companies\[].domain | string \| null | Company domain. | #### `meta` [#meta] | Field | Type | Description | | ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | in\_flight\_total | integer | True total of pending and in-progress items, regardless of pagination. | | page | integer | Current page. | | page\_size | integer | Items per page. | | page\_count | integer | Total number of pages. Above 1 only in extreme cases (e.g. a large account list being ingested) — fetch the remaining pages to see everything. | ### How the pipelines relate [#how-the-pipelines-relate] The three pipelines run in sequence: account mapping feeds top-account content, which feeds signal detection. If the list contains `account_mapping` items but no `signal` items, signals are not missing — they cannot start until the earlier stages finish. ## Errors [#errors] | Status | Meaning | | ------ | ------------------------------------ | | 401 | The API key is missing or invalid. | | 429 | Rate limit exceeded. | | 500 | An unexpected server error occurred. | Error responses use [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents with `Content-Type: application/problem+json`: ```json { "type": "https://docs.getsillage.com/errors/unauthorized", "title": "Unauthorized", "status": 401, "detail": "Invalid or missing API key.", "instance": "/api/v2/requests-status" } ``` # Account mapping (/docs/getting-started/account-mapping) Account mapping is the step where Sillage turns the accounts you added into something it can actually work on. You give it a company name or a domain. It matches that to the real company, then finds the right contacts inside it based on your targeting. Everything else depends on this. Signals and content are only as good as the mapping underneath them, so getting this step right is what makes the rest useful. ## What mapping produces [#what-mapping-produces] * **The matched company**, resolved from the name or domain you gave. * **The right contacts** inside that company, chosen against your persona. * **A status** for each account, so you can see what was found and what was not. ## When an account cannot be mapped [#when-an-account-cannot-be-mapped] Some accounts cannot be matched, for example when a domain is wrong, the company is too small to have a public footprint, or it has since closed or been acquired. Sillage flags these instead of guessing. When an account is flagged as not found, check the name or domain you provided and re-add it if it was a typo. If the company genuinely has no footprint, it is safe to leave it out, it would not produce reliable signals anyway. Mapping runs in the background after you add accounts. Wait for the list status to reach **completed** before you rely on signals or content, the data is still filling in until then. ## Where to go next [#where-to-go-next] # API quickstart (/docs/getting-started/api-quickstart) Sillage API gives client applications read access to workspace content, content requests, top accounts, and account mapping status. ## 1. Generate an API key [#1-generate-an-api-key] The API uses workspace API keys. Generate one from the Sillage app before making requests. 1. Open Sillage and select the workspace you want to query. 2. Go to **Settings → API Keys**. 3. Click **Generate API Key**. 4. Give the key a name, choose an expiration, then click **Generate**. 5. Copy the key immediately. It is only shown once. If you do not see **API Keys**, ask a workspace admin or Sillage support to enable public API key generation for your workspace. ## 2. Store the key as an environment variable [#2-store-the-key-as-an-environment-variable] ```bash export SILLAGE_API_KEY="sk_live_..." ``` Do not commit API keys to source control and do not expose them in browser code. ## 3. Make your first request [#3-make-your-first-request] ```bash curl -X POST "https://api.getsillage.com/api/v2/contents/query" \ -H "Authorization: Bearer $SILLAGE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"page_size": 5}' ``` ```js const response = await fetch('https://api.getsillage.com/api/v2/contents/query', { method: 'POST', headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ page_size: 5 }), }) const result = await response.json() ``` ```js import { request } from 'node:https' const req = request( new URL('https://api.getsillage.com/api/v2/contents/query'), { method: 'POST', headers: { Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`, 'Content-Type': 'application/json', }, }, (res) => { let body = '' res.on('data', (chunk) => { body += chunk }) res.on('end', () => { console.log(JSON.parse(body)) }) }, ) req.end(JSON.stringify({ page_size: 5 })) ``` ```python import os import requests response = requests.post( "https://api.getsillage.com/api/v2/contents/query", headers={"Authorization": f"Bearer {os.environ['SILLAGE_API_KEY']}"}, json={"page_size": 5}, timeout=30, ) response.raise_for_status() result = response.json() ``` ## 4. Browse the API reference [#4-browse-the-api-reference] Start with [Authentication](/docs/api/authentication), then use the endpoint references for workspace contents, content requests, top accounts, and account mapping. # How Sillage works (/docs/getting-started/how-sillage-works) Everything in Sillage builds toward one thing: a **signal**, a reason to reach out to an account right now. This video walks through every concept on one map, from what you set up to what you get back.