> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/pt-br/development/adding-a-new-tool.md).

# Adicionando uma Nova Ferramenta

Passo a passo completo: adicione uma nova ferramenta desde a entrada no registro até o handler da API, i18n e testes.

As "Advanced Tools" na página inicial — MAC Lookup, Whois, DNS Resolver, Censorship Check e o restante — seguem um único padrão de ligação. Esta página mostra como adicionar uma nova de ponta a ponta.

## O exemplo

Vamos adicionar um hipotético **Certificate Check**: digite um domínio e receba de volta os detalhes do certificado TLS a partir de uma API upstream.

| Parte              | Valor                                              |
| ------------------ | -------------------------------------------------- |
| Slug               | `certcheck`                                        |
| Componente         | `frontend/components/advanced-tools/CertCheck.vue` |
| Manipulador de API | `api/cert-check.js`                                |
| Rota               | `GET /api/certcheck?domain=…`                      |
| namespace de i18n  | `certcheck.*`                                      |

Ele é modelado diretamente no **MAC Lookup** ferramenta (`macchecker` → `MacChecker.vue` → `api/mac-checker.js`), que é o menor exemplo completo no repositório. Abra esses três arquivos junto com esta página.

{% hint style="info" %}
**Nem toda ferramenta precisa de um backend.** Browser Info, a Security Checklist e a IP Calculator rodam inteiramente no navegador. Se a sua também roda, pule as etapas 1–3 e vá direto para o componente — mas mantenha a lógica fora dele. A IP Calculator é o padrão: a aritmética fica em `common/ip-math.js` (exposto como `frontend/utils/ip-math.js`, porque a busca RDAP do backend o compartilha), o classificador e os analisadores próprios da calculadora em `frontend/utils/ip-calc.js`, e `IpCalculator.vue` renderiza apenas o que `calculate()` retorna. Cada módulo puro vem com sua especificação (`tests/ip-math.test.js`, `tests/ip-calc.test.js`). Veja [Ferramentas sem backend](/developer/pt-br/architecture/frontend.md#tools-without-a-backend).
{% endhint %}

## Nomeação

* **Slug** — minúsculas, sem separadores: `macchecker`, `dnsresolver`, `censorshipcheck`. É a URL em `/tools/<slug>` e a consulta do drawer `?tool=<slug>`, então é efetivamente permanente depois de lançado.
* **Componente** — PascalCase `.vue` em `frontend/components/advanced-tools/`.
* **Arquivo de manipulador** — kebab-case `.js` em `api/`.
* **Caminho da rota** — corresponde ao slug das ferramentas antigas (`/api/macchecker`), kebab-case para as mais novas (`/api/ooni-blocking`, `/api/service-status`). Ambos funcionam bem; escolha um e use-o de forma consistente.

***

{% stepper %}
{% step %}

### Escreva o manipulador da API

Um arquivo por rota em `api/`, começando com um comentário de cabeçalho que declara a rota e seu propósito. Uma única exportação padrão, `fetchUpstream` para a chamada upstream, o logger compartilhado para falhas.

{% code title="api/cert-check.js" %}

```js
// /api/certcheck — detalhes do certificado TLS para um domínio, obtidos da
// API de certificados upstream. Alimenta a ferramenta CertCheck do frontend.

import { fetchUpstream } from '../common/fetch-with-timeout.js';
import logger from '../common/logger.js';

const CERT_API_URL = 'https://example-cert-api.test/v1/cert';

export default async (req, res) => {
    if (req.method !== 'GET') {
        return res.status(405).json({ error: 'Método não permitido' });
    }

    // Presença, formato e minúsculas garantidos por requireValidDomain.
    const domain = req.query.domain;

    const token = process.env.CERT_API_KEY || '';
    if (!token) {
        return res.status(500).json({ error: 'Chave de API ausente' });
    }

    try {
        const upstream = await fetchUpstream(`${CERT_API_URL}?host=${domain}&key=${token}`);
        if (!upstream.ok) {
            throw new Error(`A API de certificados respondeu com o status ${upstream.status}`);
        }
        res.json(await upstream.json());
    } catch (error) {
        logger.error({ err: error, domain }, 'falha no manipulador de cert-check');
        res.status(500).json({ error: error.message });
    }
};
```

{% endcode %}

Quatro coisas que não são negociáveis:

* **`fetchUpstream`, nunca um simples `fetch()`.** Ele carrega o timeout de 8 segundos e o `User-Agent`.
* **O logger compartilhado, nunca `console.*`.** Objeto de contexto primeiro, mensagem curta depois.
* **Formatos de erro concisos.** `400` em caso de entrada inválida, `500` com `{ error: … }` em caso de falha.
* **Sem verificações de referer ou de parâmetro no manipulador.** O middleware já fez isso — veja a próxima etapa.

A `req.method !== 'GET'` barreira é defensiva: a rota abaixo já restringe o método. Ela permanece porque o teste smoke a verifica diretamente.
{% endstep %}

{% step %}

### Reutilize uma guarda — ou adicione uma

A validação de parâmetros fica em `common/guards.js`, nunca dentro dos handlers. Nossa ferramenta recebe `?domain=`, e essa guarda já existe:

```js
requireValidDomain()   // rejeita domínios ausentes/malformados, converte para minúsculas no próprio valor
```

Converter para minúsculas no próprio valor importa: o cache de borda usa a URL como chave, então uma consulta com letras maiúsculas e minúsculas misturadas não pode virar uma entrada separada no cache.

O conjunto completo disponível hoje:

| Guarda                     | Valida                                               |
| -------------------------- | ---------------------------------------------------- |
| `requireReferer`           | Global em `/api/*` — domínios permitidos + localhost |
| `requirePublicIP()`        | `?ip=` — bem formado **e** roteável publicamente     |
| `requireValidDomain()`     | `?domain=` (também converte para minúsculas)         |
| `requireValidPrefix()`     | `?prefix=` (CIDR)                                    |
| `requireValidASN()`        | `?asn=` (remove `AS`, reescreve para numérico)       |
| `requireValidProviderId()` | `?id=` na lista de slugs do service-status           |
| `requireValidRecordType()` | `?type=` na lista de अनुमति de tipos de registro DNS |
| `requireValidReportId()`   | `/api/report/:id` parâmetro de rota                  |

Observe que `requirePublicIP()` faz duas perguntas, não uma: rejeita um endereço malformado com `Endereço IP inválido`, e um bem formado, mas reservado — `192.168.1.1`, `127.0.0.1`, qualquer coisa em RFC 1918 / CGNAT / link-local / espaço de documentação — com `Não é um endereço IP público`. Essa é a guarda correta para uma rota cujo upstream é um registro ou uma fonte de geolocalização, porque eles não têm nada a dizer sobre um endereço privado. Se a sua ferramenta realmente aceita um endereço privado (algo que inspeciona um alvo na LAN, por exemplo), essa guarda é a errada: escreva ao lado dela uma guarda apenas de validade, em vez de flexibilizar esta, da qual sete rotas de geolocalização dependem.

{% hint style="warning" %}
**Um novo formato de parâmetro significa uma nova guarda.** Adicione-a como uma factory exportada em `common/guards.js`, conecte-a em `backend-server.js`, e cubra-a em `tests/guards.test.js`. Não implemente a verificação dentro do seu handler — é exatamente esse desvio que a camada de guards existe para evitar.
{% endhint %}
{% endstep %}

{% step %}

### Conecte a rota em `backend-server.js`

Cada rota do app é declarada neste único arquivo. Importe o handler no topo, junto com os outros:

{% code title="backend-server.js" %}

```js
import certCheckHandler from './api/cert-check.js';
```

{% endcode %}

Depois declare a rota. A ordem do middleware é guarda primeiro, depois cache, depois handler:

{% code title="backend-server.js" %}

```js
app.get('/api/certcheck', requireValidDomain(), cacheable(ONE_DAY_CACHE), certCheckHandler);
```

{% endcode %}

Escolha o TTL de acordo com a velocidade real de mudança dos dados upstream. O arquivo já define as constantes — `FIVE_MIN_CACHE`, `ONE_HOUR_CACHE`, `ONE_DAY_CACHE`, `SEVEN_DAYS_CACHE`, `THIRTY_DAYS_CACHE`, `ONE_YEAR_CACHE` — todas escritas como expressões multiplicadas, e não como segundos brutos.

Se os dados forem por usuário, autenticados ou mudarem a cada requisição, **omita `cacheable()` por completo**. Tudo sob `/api/*` usa por padrão `Cache-Control: no-store`, então deixá-lo de fora é a escolha segura.

Nova variável de ambiente? Adicione-a a `.env.example` com um comentário, e documente-a em [Variáveis de Ambiente](/developer/pt-br/reference/environment-variables.md) e [Chaves de API opcionais](/developer/pt-br/configuration/optional-api-keys.md).
{% endstep %}

{% step %}

### Construa o componente

Crie `frontend/components/advanced-tools/CertCheck.vue`. Ele é um `<script setup>` componente normal — o drawer e a página independente o montam como está, então ele não precisa de moldura extra, nem barra de título, nem consciência de rota.

Copie os padrões canônicos em vez de inventar novos. De `MacChecker.vue`:

{% code title="frontend/components/advanced-tools/CertCheck.vue" %}

```vue
<template>
    <div class="cert-check-section my-4 space-y-4">
        <p class="text-sm text-muted-foreground leading-relaxed">{{ t('certcheck.Note') }}</p>

        <div class="space-y-2">
            <Label for="queryDomain">{{ t('certcheck.Note2') }}</Label>
            <div class="flex items-center gap-2">
                <Input type="text" id="queryDomain" name="queryDomain"
                    autocomplete="off" autocorrect="off" autocapitalize="off"
                    spellcheck="false" data-1p-ignore data-lpignore="true"
                    :disabled="status === 'running'"
                    :placeholder="t('certcheck.Placeholder')"
                    v-model="queryDomain" @keyup.enter="onSubmit" />
                <Button variant="action" :disabled="status === 'running' || !queryDomain"
                    @click="onSubmit" class="cursor-pointer">
                    <Spinner v-if="status === 'running'" />
                    <Search v-else class="size-4 shrink-0" />
                </Button>
            </div>
            <p v-if="errorMsg" class="text-sm text-destructive">{{ errorMsg }}</p>
        </div>

        <!-- Área de resultado -->
        <Card v-if="result.subject">…</Card>
    </div>
</template>

<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { trackEvent } from '@/utils/analytics';
import { Search } from '@lucide/vue';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Spinner } from '@/components/ui/spinner';
import { Label } from '@/components/ui/label';

const { t } = useI18n();

const queryDomain = ref('');
const status = ref('idle');
const result = ref({});
const errorMsg = ref('');

const onSubmit = () => {
    trackEvent('Section', 'StartClick', 'CertCheck');
    errorMsg.value = '';
    result.value = {};
    if (queryDomain.value) fetchCert(queryDomain.value);
};

const fetchCert = async (domain) => {
    status.value = 'running';
    try {
        const response = await fetch(`/api/certcheck?domain=${domain}`);
        if (!response.ok) throw new Error('A resposta da rede não estava ok');
        result.value = await response.json();
    } catch (error) {
        console.error('Erro ao buscar certificado:', error);
        errorMsg.value = t('certcheck.fetchError');
    } finally {
        status.value = 'idle';
    }
};
</script>
```

{% endcode %}

Coisas que valem destacar:

* **Botão de acionamento** — `variant="action"` com `<Spinner v-if />` e um `:disabled` guard. Isso é o affordance "execute isto" válido em todo o projeto.
* **Campos à prova de preenchimento automático** — todo `Input` livre carrega todos os seis atributos mostrados acima, e o placeholder evita a palavra "endereço" (e suas traduções) porque o QuickType do iOS usa a própria palavra como chave, mesmo com `autocomplete="off"`.
* **`console.*` está tudo bem aqui.** O frontend o usa; apenas arquivos de backend são restritos.
* **Sem switches de estado→cor feitos na mão.** Se sua ferramenta mapeia um estado de negócio para uma cor, use `composables/use-status-tone.js`.
* **Cada string é uma `t()` chamada.** Nada visível ao usuário fica codificado diretamente.

Mais padrões canônicos — cartões de status, flags, tabelas vs. listas, cabeçalhos de diálogo, motion — estão catalogados em [Frontend](/developer/pt-br/architecture/frontend.md).
{% endstep %}

{% step %}

### Registre a ferramenta

`frontend/data/tools.js` é a única fonte da verdade. Uma entrada ali dá a você o card na página inicial, o drawer inferior, a `/tools/<slug>` página independente e o item do menu de navegação — **você não mexe no router**. (A página independente é a única parte da qual uma ferramenta pode optar por sair; veja `noStandalone` abaixo.)

{% code title="frontend/data/tools.js" %}

```js
export const ADVANCED_TOOLS = [
  // …entradas existentes…
  { slug: 'certcheck', emoji: '🔐', titleKey: 'certcheck.Title', noteKey: 'advancedtools.CertCheck', component: () => import('@/components/advanced-tools/CertCheck.vue') },
];
```

{% endcode %}

A estrutura da entrada:

<table><thead><tr><th width="230">Campo</th><th>Significado</th></tr></thead><tbody><tr><td><code>slug</code></td><td>Identificador estável de URL — <code>/tools/&#x3C;slug></code> e a <code>?tool=&#x3C;slug></code> consulta do drawer.</td></tr><tr><td><code>emoji</code></td><td>Ícone do card e ícone do cabeçalho do drawer.</td></tr><tr><td><code>titleKey</code></td><td>chave de i18n para o título da ferramenta.</td></tr><tr><td><code>noteKey</code></td><td>chave de i18n para a descrição de uma linha do card.</td></tr><tr><td><code>component</code></td><td>Importação preguiçosa do <code>.vue</code> arquivo — usado tanto pelo drawer quanto pela página independente.</td></tr><tr><td><code>requiresOriginalSite</code></td><td>Opcional. <code>true</code> oculta a ferramenta em instâncias auto-hospedadas. Omitir para uma ferramenta pública.</td></tr><tr><td><code>noStandalone</code></td><td>Opcional. <code>true</code> significa que a ferramenta não tem <code>/tools/&#x3C;slug></code> página — o menu suspenso da página inicial é seu único ponto de entrada. Omita isso para uma ferramenta normal.</td></tr></tbody></table>

A ordem no array é a ordem dos cards na página inicial.

{% hint style="info" %}
**`requiresOriginalSite: true`** é para ferramentas que dependem da API privada do IPCheck.ing e de uma conta autenticada — elas ficam ocultas em forks e instâncias auto-hospedadas, que não têm como acessar esse backend. Veja [Recursos vinculados ao IPCheck.ing](/developer/pt-br/configuration/features-tied-to-ipcheck-ing.md). A maioria das ferramentas novas deve omitir este campo.
{% endhint %}

{% hint style="info" %}
**`noStandalone: true`** é para ferramentas que leem o estado que a página inicial controla. `StandaloneTool.vue` trata esse slug como não registrado e redireciona `/tools/<slug>` para `/?tool=<slug>`, o card linka diretamente para lá, e o menu suspenso oculta o ícone de abrir em nova aba. A verificação detalhada de Persona é o único exemplo atual: ela cruza os resultados dos testes da página inicial, então uma página que saísse deles não teria com o que trabalhar. Uma ferramenta autocontida — que é quase todas — deve omitir este campo e manter sua página compartilhável e indexável.
{% endhint %}
{% endstep %}

{% step %}

### Adicione a cópia — em cada `pacote` localidade

Toda string que você referenciou precisa de uma entrada em cada `pacote` pacote da localidade `frontend/locales/` — toda localidade `common/locale-registry.js` marca `pacote`; esse arquivo é a lista atual. Todas elas, na mesma alteração. Esta não é uma tarefa de acompanhamento.

Localidades marcadas `beta` no registro estão isentos: uma chave que você omitir deles volta para o inglês automaticamente. Deixe a chave presente como `""` se `pnpm i18n-sync` coloca isso lá.

Dois lugares para editar por localidade — o namespace da sua própria ferramenta:

{% code title="frontend/locales/en.json" %}

```json
"certcheck": {
  "Title": "Verificação de Certificado",
  "Note": "Consulte o certificado TLS de qualquer domínio: emissor, período de validade e nomes alternativos do sujeito.",
  "Note2": "Digite um domínio para iniciar a verificação:",
  "Placeholder": "exemplo.com",
  "fetchError": "Não foi possível buscar os detalhes do certificado"
}
```

{% endcode %}

…e a descrição do card no namespace compartilhado `advancedtools` namespace:

{% code title="frontend/locales/en.json" %}

```json
"advancedtools": {
  "CertCheck": "Inspecione o certificado TLS de um domínio"
}
```

{% endcode %}

O namespace normalmente corresponde ao slug (`macchecker`, `dnsresolver`, `censorshipcheck`). Mantenha os nomes das chaves idênticos em todos os pacotes — `en.json` é a referência e os demais mantêm exatamente os mesmos caminhos de chave; só os valores mudam.

Detalhes sobre carregamento, subpacotes e a cadeia de fallback: [i18n](/developer/pt-br/development/i18n.md).
{% endstep %}

{% step %}

### Adicione uma entrada no changelog

Acrescente sua entrada ao **último** bloco de versão em `frontend/data/changelog.json` — o arquivo é executado do mais antigo para o mais novo e a interface o renderiza ao contrário.

{% code title="frontend/data/changelog.json" %}

```json
{
  "type": "add",
  "change": {
    "en": "Nova ferramenta de Verificação de Certificado: inspecione o certificado TLS de qualquer domínio",
    "zh": "新增证书检查工具：查看任意域名的 TLS 证书",
    "zh-TW": "新增憑證檢查工具：查看任意網域的 TLS 憑證",
    "fr": "Nouvel outil de vérification de certificat : inspectez le certificat TLS de n'importe quel domaine",
    "ru": "Новый инструмент проверки сертификатов: просмотр TLS-сертификата любого домена"
  }
}
```

{% endcode %}

`tipo` deve ser um dos `add`, `improve`, ou `fix`. Toda `pacote` string da localidade precisa estar presente e não vazia — `tests/changelog.test.js` falha na compilação caso contrário. Localidades beta estão isentas do histórico do changelog.
{% endstep %}

{% step %}

### Escreva os testes

Dois tipos, ambos em `tests/`.

**Testes de fumaça do handler** vão em `tests/api-handlers.test.js`, em um bloco `describe` ao lado dos outros. Afirme apenas nos ramos que retornam **antes da** primeira `fetchUpstream` chamada — a suíte nunca toca um upstream real:

{% code title="tests/api-handlers.test.js" %}

```js
import certCheckHandler from '../api/cert-check.js';

// -- cert-check handler ---------------------------------------------------
// A presença/forma do domínio é imposta pelo middleware requireValidDomain
// (tests/guards.test.js); os próprios ramos pré-busca do handler são o
// gate do método e o retorno antecipado por falta de chave de API.

describe('cert-check handler', () => {
    it('rejeita métodos diferentes de GET com 405 antes de atingir o upstream', async () => {
        const res = createResponse();
        await certCheckHandler(createRequest({ method: 'POST', query: { domain: 'example.com' } }), res);
        assert.equal(res.statusCode, 405);
        assert.equal(res.body.error, 'Method Not Allowed');
    });

    it('retorna 500 quando a chave de API está ausente', async () => {
        delete process.env.CERT_API_KEY;
        const res = createResponse();
        await certCheckHandler(createRequest({ query: { domain: 'example.com' } }), res);
        assert.equal(res.statusCode, 500);
        assert.equal(res.body.error, 'API key missing');
    });
});
```

{% endcode %}

O arquivo já fornece `createRequest()` / `createResponse()` stubs. Se o seu handler ler uma nova variável de ambiente, adicione o nome dela ao array `ENV_KEYS` no topo para que os ganchos de backup/restauração a cubram.

**Especificações unitárias** cobrem qualquer lógica pura que sua ferramenta introduza — validadores, parsers, transformações, um composable com entradas simuláveis. Dê a cada uma seu próprio `tests/<subject>.test.js`. Se você adicionou um guard, estenda `tests/guards.test.js`.

O que *não* recebe teste: renderização Vue, chamadas reais de rede, APIs do navegador. Veja [Testes](/developer/pt-br/development/testing.md).
{% endstep %}

{% step %}

### Execute a autoverificação

```bash
pnpm check
```

Testes mais uma compilação de produção. Precisa estar verde antes de você abrir um PR.

Depois, veja a ferramenta você mesmo em `pnpm dev` — na grade de cards da página inicial, no menu suspenso e em `/tools/certcheck` — porque nada disso pode ser verificado por máquina. Diga isso na descrição do PR.
{% endstep %}
{% endstepper %}

***

## Checklist

| Etapa                                                  | Arquivo                                                                                     |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| Handler                                                | `api/cert-check.js`                                                                         |
| Guard (somente se houver um novo formato de parâmetro) | `common/guards.js`                                                                          |
| Rota                                                   | `backend-server.js`                                                                         |
| Componente                                             | `frontend/components/advanced-tools/CertCheck.vue`                                          |
| Entrada no registro                                    | `frontend/data/tools.js`                                                                    |
| Cópia, em cada `pacote` localidade                     | `frontend/locales/<code>.json`                                                              |
| Changelog, em cada `pacote` localidade                 | `frontend/data/changelog.json`                                                              |
| Teste de fumaça                                        | `tests/api-handlers.test.js`                                                                |
| Especificações unitárias                               | `tests/*.test.js`                                                                           |
| Nova variável de ambiente                              | `.env.example`                                                                              |
| Atalho da página inicial (opcional)                    | `frontend/composables/use-shortcuts.js` + `shortcutKeys.<Tool>` em cada `pacote` localidade |

## Indo além

Duas pequenas facilidades que a maioria das ferramentas quer:

* **Um atalho na página inicial.** Uma linha em `frontend/composables/use-shortcuts.js` — `{ keys: 'a', action: () => goToAdvancedTool('ipcalculator', 'IpCalculator'), description: t('shortcutKeys.IpCalculator') }` é do Calculador de IP — além da `shortcutKeys.<Tool>` string em cada `pacote` localidade, que é o que o <kbd>?</kbd> painel de ajuda lista. `goToAdvancedTool` rola até a seção Ferramentas Avançadas e abre o menu suspenso nesse slug. Atalhos funcionam apenas na rota inicial e ficam suspensos enquanto qualquer sobreposição estiver aberta; escolha uma tecla que ainda esteja livre nesse arquivo (letras únicas diferenciam maiúsculas e minúsculas: `M` e `m` são atalhos diferentes).
* **Uma entrada compartilhável.** Se um resultado vale a pena ser linkado, leia `route.query.q` na montagem e execute-o, e escreva a consulta atual de volta com `router.replace` após cada execução — nunca `push`, ou o histórico cresce uma entrada por execução. Isso funciona em ambos os pontos de entrada (`/tools/<slug>?q=` e `/?tool=<slug>&q=`). `IpCalculator.vue` é a referência.

Três sistemas opcionais nos quais sua ferramenta pode se integrar. Os dois primeiros são orientados a eventos — os componentes emitem no `utils/app-events.js` barramento e nunca chamam os sistemas diretamente:

* **Conquistas.** Emita um evento de domínio, mapeie-o para um slug de conquista em `frontend/data/achievement-rules.js`, e adicione a conquista em `frontend/data/achievements.js`.
* **Relatórios de diagnóstico compartilháveis.** Um teste "my network" emite `<domain>:finished` com seu resultado estruturado; um construtor em `frontend/utils/report-builders.js` o normaliza, e `common/report-schema.js` autoriza os campos. Os construtores falham de forma suave, então uma entrada de esquema ausente aparece como um campo simplesmente não presente, em vez de um erro — adicione o construtor e a entrada de esquema na mesma alteração.
* **O barramento de comandos.** Se outras partes do app devem conseguir *acionar* sua ferramenta — um atalho de teclado, o orquestrador de atualização, o assistente de documentação — registre um comando na configuração via `composables/use-app-command.js` em vez de expor métodos por meio de refs de template. Uma ferramenta montada no menu suspenso só possui seu comando enquanto sua rota `?tool=` estiver aberta, então os chamadores combinam `waitForAppCommand` com o disparo.

Todos os três são descritos com mais detalhes em [Frontend](/developer/pt-br/architecture/frontend.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/pt-br/development/adding-a-new-tool.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
