> ## Documentation Index
> Fetch the complete documentation index at: https://strattumai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Skills API

> Execução de receitas analíticas (skills), gerenciamento de segredos e perguntas sugeridas para o assistente.

A **Skills API** é o motor de execução de receitas analíticas da strattum.ai. Cada skill é um script versionado no Workspace Git que os agentes executam — via Console, API ou MCP Server — para responder perguntas de negócio recorrentes e rodar automações.

## Base URL

```
http://localhost:8007
```

## Autenticação

A Skills API é acessada internamente pelo console da strattum.ai via BFF (Next.js). A versão 1.0 não exige autenticação direta — o acesso é controlado pela rede privada do cliente.

## Endpoints

<CardGroup cols={2}>
  <Card title="GET /skills" icon="list" href="#get-skills">
    Lista todas as skills disponíveis no workspace.
  </Card>

  <Card title="POST /skills" icon="plus" href="#post-skills">
    Cria uma nova skill.
  </Card>

  <Card title="GET /secrets" icon="key" href="#get-secrets">
    Lista as chaves de segredos configurados (nunca os valores).
  </Card>

  <Card title="POST /secrets" icon="lock" href="#post-secrets">
    Cria ou atualiza um segredo.
  </Card>
</CardGroup>

***

## GET /skills

Lista todas as skills disponíveis no workspace atual.

### Exemplos de chamada

<CodeGroup>
  ```bash cURL theme={null}
  curl http://localhost:8007/skills
  ```

  ```python Python theme={null}
  import httpx

  resp = httpx.get("http://localhost:8007/skills")
  data = resp.json()
  print(f"{data['total']} skills disponíveis")
  for skill in data["skills"]:
      print(skill["name"], "-", skill["description"])
  ```

  ```javascript JavaScript theme={null}
  const resp = await fetch('http://localhost:8007/skills')
  const { skills, total } = await resp.json()
  ```
</CodeGroup>

### Resposta

**200 OK**

```json theme={null}
{
  "skills": [
    {
      "name": "tickets_em_aberto",
      "description": "Lista os tickets de suporte em aberto por cliente e prioridade.",
      "steps": [
        {
          "type": "sql",
          "query": "SELECT * FROM main_clean.fct_tickets WHERE status = 'open' ORDER BY priority DESC"
        }
      ],
      "tags": ["suporte", "tickets"]
    }
  ],
  "total": 1
}
```

<ResponseField name="skills" type="array">
  Lista de skills disponíveis.

  <Expandable title="Campos de cada skill">
    <ResponseField name="name" type="string">
      Identificador único da skill (snake\_case). Usado como nome da tool MCP exposta ao assistente.
    </ResponseField>

    <ResponseField name="description" type="string">
      Descrição legível da skill. O assistente usa este texto para decidir quando chamar a skill.
    </ResponseField>

    <ResponseField name="steps" type="array">
      Passos de execução da skill. Cada passo tem `type` (`sql`, `cypher`, `code`) e `query`/`code`.
    </ResponseField>

    <ResponseField name="tags" type="array">
      Tags para organização e filtro das skills.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="integer">
  Total de skills disponíveis.
</ResponseField>

***

## POST /skills

Cria uma nova skill no workspace.

### Body (`application/json`)

<ParamField body="name" type="string" required>
  Identificador único da skill em snake\_case. Exemplo: `receita_por_cliente`.
</ParamField>

<ParamField body="description" type="string" required>
  Descrição clara do que a skill faz. O assistente usa este texto para decidir quando utilizá-la.
</ParamField>

<ParamField body="steps" type="array" required>
  Lista de passos de execução. Cada passo deve ter `type` (`sql`, `cypher` ou `code`) e o conteúdo correspondente.
</ParamField>

<ParamField body="tags" type="array">
  Tags opcionais para organização.
</ParamField>

### Exemplos de chamada

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST http://localhost:8007/skills \
    -H "Content-Type: application/json" \
    -d '{
      "name": "receita_por_cliente",
      "description": "Calcula a receita total por cliente nos últimos 90 dias.",
      "steps": [
        {
          "type": "sql",
          "query": "SELECT cliente_id, SUM(valor) as receita FROM main_clean.fct_pedidos WHERE data_pedido >= CURRENT_DATE - INTERVAL 90 DAY GROUP BY cliente_id ORDER BY receita DESC LIMIT 20"
        }
      ],
      "tags": ["financeiro", "clientes"]
    }'
  ```

  ```python Python theme={null}
  import httpx

  resp = httpx.post("http://localhost:8007/skills", json={
      "name": "receita_por_cliente",
      "description": "Calcula a receita total por cliente nos últimos 90 dias.",
      "steps": [
          {
              "type": "sql",
              "query": "SELECT cliente_id, SUM(valor) as receita FROM main_clean.fct_pedidos WHERE data_pedido >= CURRENT_DATE - INTERVAL 90 DAY GROUP BY cliente_id ORDER BY receita DESC LIMIT 20",
          }
      ],
      "tags": ["financeiro", "clientes"],
  })
  skill = resp.json()
  print("Skill criada:", skill["name"])
  ```
</CodeGroup>

### Resposta

**201 Created**

```json theme={null}
{
  "name": "receita_por_cliente",
  "description": "Calcula a receita total por cliente nos últimos 90 dias.",
  "steps": [...],
  "tags": ["financeiro", "clientes"]
}
```

***

## GET /secrets

Lista as **chaves** dos segredos configurados no workspace. Os valores nunca são retornados por este endpoint.

### Exemplos de chamada

<CodeGroup>
  ```bash cURL theme={null}
  curl http://localhost:8007/secrets
  ```
</CodeGroup>

### Resposta

**200 OK**

```json theme={null}
{
  "secrets": [
    {
      "key": "OPENAI_API_KEY",
      "description": "Chave da API OpenAI para geração de embeddings"
    },
    {
      "key": "SLACK_BOT_TOKEN",
      "description": "Token do bot Slack para notificações"
    }
  ]
}
```

<ResponseField name="secrets" type="array">
  Lista de segredos configurados, contendo apenas chave e descrição — nunca o valor.
</ResponseField>

***

## POST /secrets

Cria ou atualiza um segredo no workspace. Se a chave já existir, o valor é sobrescrito.

### Body (`application/json`)

<ParamField body="key" type="string" required>
  Nome da variável de ambiente ou identificador do segredo. Exemplo: `OPENAI_API_KEY`.
</ParamField>

<ParamField body="value" type="string" required>
  Valor do segredo. Armazenado de forma segura — não é retornado após gravação.
</ParamField>

<ParamField body="description" type="string">
  Descrição opcional do uso do segredo.
</ParamField>

### Exemplos de chamada

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST http://localhost:8007/secrets \
    -H "Content-Type: application/json" \
    -d '{
      "key": "SLACK_BOT_TOKEN",
      "value": "xoxb-...",
      "description": "Token do bot Slack para notificações de alertas"
    }'
  ```

  ```python Python theme={null}
  import httpx

  resp = httpx.post("http://localhost:8007/secrets", json={
      "key": "SLACK_BOT_TOKEN",
      "value": "xoxb-...",
      "description": "Token do bot Slack para notificações de alertas",
  })
  print(resp.status_code)
  ```
</CodeGroup>

### Resposta

**200 OK** — segredo salvo

```json theme={null}
{ "key": "SLACK_BOT_TOKEN", "saved": true }
```

**400 Bad Request** — campos obrigatórios ausentes

```json theme={null}
{ "error": "key and value are required" }
```

<Warning>
  Os valores de segredos nunca são retornados pela API após gravação. Se você perder um valor, precisará sobrescrevê-lo com um novo.
</Warning>

***

## Perguntas sugeridas

O endpoint de perguntas sugeridas é exposto pelo BFF do console e utilizado internamente para apresentar sugestões contextuais ao usuário.

### GET /api/skills/suggested-questions

Retorna perguntas sugeridas com base nos conectores ativos do workspace.

**Parâmetros de query:**

<ParamField query="connector_ids" type="string">
  IDs de conectores separados por vírgula. Quando omitido, retorna sugestões globais.
</ParamField>

**Resposta:**

```json theme={null}
{
  "questions": [
    "Quais são os tickets em aberto com prioridade alta?",
    "Qual cliente gerou mais receita nos últimos 30 dias?",
    "Quais contatos não foram contactados nos últimos 60 dias?"
  ]
}
```
