Docs
...

Expor ações

Um app que expõe ações descreve, em um único documento, o que ele sabe fazer: cada ação tem um nome, uma permissão exigida, um nível de autenticação e uma operação HTTP. Com esse manifesto de ações, três consumidores passam a usar o seu app sem código específico:

  • o Workflow oferece cada ação como um nó de automação (e cada gatilho como um evento);
  • o Iugu for AI oferece cada ação como uma ferramenta para assistentes de IA (<tag>__<nome>), chamada em nome da pessoa que conversa com o assistente;
  • a CLI iugu lista e chama as ações (iugu app actions list|call) durante o desenvolvimento, e chama ações de apps provedores como a pessoa com iugu call <app> <ação>.

Pré-requisitos

  1. Na aba Privilégios, marque Actions Provider e informe a URL da API de Serviço (uma URL base, por exemplo https://acme.example/api).
  2. Na aba Permissões, declare como ações implementadas todas as permissões que o manifesto vai citar (invoice.create, invoice.read…). O Console só aceita uma ação cujo authorization.action seja uma das ações implementadas do app.
  3. Sirva o manifesto em GET {URL da API de Serviço}/actions — público (nada secreto dentro), Content-Type: application/json, de preferência com Cache-Control: max-age=300 e ETag.

O Console lê o manifesto ao salvar a URL da API de Serviço, os privilégios ou as ações implementadas, e o mantém em cache respeitando ETag/Cache-Control. Limites: 256 KB, 4 s, sem redirecionamentos para outra origem.

O manifesto (iugu.actions/v1)

{
  "spec": "iugu.actions/v1",
  "app": {
    "client_id": "6cMyiqcbF1bDiVz0SfULTk",
    "tag": "acme-invoices",
    "name": "Acme Invoices",
    "service_api_url": "https://acme.example/api",
    "docs_url": "https://acme.example/docs"
  },
  "actions": [
    {
      "name": "create_invoice",
      "title": "Emitir fatura",
      "label": "Emite uma fatura em aberto para um cliente da área de trabalho",
      "description": "Emite uma fatura em aberto. Valor em centavos (BRL). Exige um segundo fator recente (config).",
      "authorization": { "action": "acme-invoices:invoice.create", "acr": "config" },
      "url": "https://acme.example/api/invoices",
      "method": "POST",
      "annotations": { "readOnlyHint": false, "destructiveHint": true, "idempotentHint": false, "openWorldHint": false },
      "parameters": [
        { "name": "customer",     "type": "string",   "required": true,  "direction": "ParamInput",  "source": "body", "description": "Nome do cliente", "label": "Cliente" },
        { "name": "amount_cents", "type": "integer",  "required": true,  "direction": "ParamInput",  "source": "body", "description": "Valor em centavos (BRL)", "label": "Valor", "format": "money_cents" },
        { "name": "description",  "type": "textarea", "required": false, "direction": "ParamInput",  "source": "body", "description": "Texto livre", "label": "Descrição" },
        { "name": "id",           "type": "string",   "required": true,  "direction": "ParamOutput", "description": "Id da fatura" },
        { "name": "status",       "type": "select",   "required": true,  "direction": "ParamOutput", "description": "open|paid",
          "options": [ { "value": "open", "label": "Em aberto" }, { "value": "paid", "label": "Paga" } ] }
      ]
    },
    {
      "name": "list_invoices",
      "title": "Listar faturas",
      "label": "Faturas da área de trabalho, mais recentes primeiro",
      "authorization": { "action": "acme-invoices:invoice.read" },
      "url": "https://acme.example/api/invoices",
      "method": "GET",
      "parameters": [
        { "name": "data", "type": "json", "required": true, "direction": "ParamOutput", "description": "Lista de faturas" }
      ]
    }
  ],
  "triggers": [
    {
      "name": "invoice_paid",
      "title": "Fatura paga",
      "label": "Dispara quando uma fatura é paga",
      "authorization": { "action": "acme-invoices:invoice.read" },
      "parameters": [
        { "name": "id",           "type": "string",  "required": true, "direction": "ParamOutput", "description": "Id da fatura" },
        { "name": "amount_cents", "type": "integer", "required": true, "direction": "ParamOutput", "description": "Valor" }
      ]
    }
  ]
}

No nível mais alto: spec (obrigatório, iugu.actions/v1), app (recomendado), actions[] e triggers[] (qualquer um pode estar vazio). Chaves desconhecidas são ignoradas pelos consumidores; dentro da v1 só há mudanças aditivas.

Ação

Campo Obrigatório Significado
name sim Identificador estável em snake_case, único no manifesto. É o que o Workflow, o Iugu for AI (acme_invoices__create_invoice) e a CLI usam.
title sim Título curto para pessoas.
label sim Uma linha de descrição, exibida em listas.
description não Texto mais longo para agentes (vira a descrição da ferramenta MCP).
icon não URL absoluta (SVG/PNG).
authorization sim action (obrigatório): a permissão <tag>:<ação> que o principal precisa ter na área de trabalho — uma das ações implementadas do app. acr (opcional): informations (padrão) · config · transfers — o nível de autenticação que a pessoa precisa ter; consumidores sem pessoa (um app agindo por si) não conseguem satisfazer config/transfers.
url sim Absoluta, na mesma origem da URL da API de Serviço (qualquer outra é recusada); :nome marca parâmetros de caminho.
method sim GET · POST · PUT · PATCH · DELETE
annotations não Anotações de ferramenta MCP. Padrões: GETreadOnlyHint: true, idempotentHint: true; demais → destructiveHint: true.
parameters sim Entradas e saídas (abaixo).
input_schema, output_schema não JSON Schema que substitui o derivado de parameters (para corpos que não são objetos planos).
confirmation não Só em ações elevadas: { "url": "https://…/preview" } — uma leitura que o Console faz para mostrar à pessoa o que esta chamada vai fazer (veja Confirmação).
widget não Transforma uma ação de leitura em um cartão do painel do Console (veja Widgets).

Parâmetro

Campo Obrigatório Significado
name sim snake_case; para source: path, corresponde ao :nome na url.
type sim string · textarea · number · integer · boolean · datetime · json · select · color · lookup
required sim  
direction sim ParamInput (entrada) · ParamOutput (saída)
description sim  
source entradas query · path · body (padrão: body fora de GET, query em GET)
default não Valor padrão.
options select [{ "value", "label" }]enum no JSON Schema.
lookup lookup { "endpoint", "method", "params" } — opções dinâmicas, consultadas com um token de app.
hidden, disabled, alias não Dicas para o editor do Workflow; parâmetros hidden não viram entradas de ferramenta.
label, format, items não Dicas de exibição para a confirmação e a Auditoria (abaixo).

O JSON Schema das ferramentas é derivado de parameters (string/textarea/color/lookupstring; number; integer; boolean; datetimestring com format: date-time; jsonobject/array; selectstring com enum; required: truerequired[]).

Gatilho

name (o valor de event que o app envia ao Workflow), title, label, icon, description, authorization.action e parameters (todos ParamOutput). O Iugu for AI ignora gatilhos na v1; o Workflow os usa como eventos.

Atender uma chamada

Toda chamada a uma ação chega ao seu backend assim:

   
Authorization Bearer <JWT> com aud contendo Iugu.Platform.<id do seu app>
Workspace id curto da área de trabalho em que a ação é executada
Content-Type application/json quando há corpo; as entradas vão para query, path ou body conforme source

O que o seu backend faz, nesta ordem:

  1. Valida o JWT: JWKS em https://identity.iugu.com/.well-known/jwks.json, iss = https://identity.iugu.com/, aud, typ = at+JWT, exp.
  2. Verifica a permissão do sub com POST /verify (workspace_id = o cabeçalho Workspace, principals: [token.sub], actions: [authorization.action]). O sub é a pessoa (user:…) — quando a chamada vem do Iugu for AI ou do Workflow em nome dela — ou um app agindo por si (app:…). O client_id do token é só atribuição (iugu-for-ai, um workflow): registre-o, não autorize por ele.
  3. Aplica o nível para sub do tipo user:: se a claim acr do token for menor que o acr da ação, responda 401 com WWW-Authenticate: Bearer error="insufficient_user_authentication", acr_values="urn:iugu:grant_scopes:config" (RFC 9470). O Iugu for AI mostra à pessoa um cartão de confirmação e repete a chamada com um token elevado.
  4. Responde um objeto JSON cujas chaves de primeiro nível são os nomes dos parâmetros ParamOutput. Erros no formato RFC 9457 (error, error_description).

Warning

Tokens do Iugu for AI para o seu app duram 2 minutos e trazem o sub da pessoa. Não use /userinfo para autorizar uma chamada de ponte: verifique o sub com /verify.

Confirmação de ações elevadas

Quando uma ação exige config ou transfers, o Iugu for AI não a executa direto: a pessoa recebe um link para uma página do Console que mostra o app, a ação, os argumentos exatos, a área de trabalho e quem pediu — e confirma com o segundo fator. A confirmação vale para uma repetição com os mesmos argumentos. Duas coisas melhoram o que a pessoa vê:

Dicas de exibição nos parâmetros de entrada: label (≤ 40 caracteres, no lugar da chave), format (money_cents, money, date, datetime, percent, count, text) e, para um json que é uma lista de registros, items com as colunas na ordem desejada:

{ "name": "invoices", "type": "json", "required": true, "direction": "ParamInput", "source": "body", "label": "Faturas",
  "items": { "customer": { "label": "Cliente" }, "amount_cents": { "label": "Valor", "format": "money_cents" },
             "due_date": { "label": "Vencimento", "format": "date" } } }

O Console faz toda a renderização e os totais (uma coluna money_cents ganha soma automaticamente); uma dica pode rotular um valor, nunca escondê-lo.

Prévia com confirmation.url: para ações cujos argumentos são só ids (publicar a versão X de um plano), aponte uma leitura GET na mesma origem, com os mesmos :placeholders de caminho da ação. O Console a chama como a pessoa, ao montar a página, e exibe o retorno em “O que esta chamada vai fazer”:

{ "summary": "Publica a versão 3 do plano Pro com 4 preços",
  "items": [ { "label": "Plano", "value": "Pro" }, { "label": "Vigência", "value": "01/10/2026" } ],
  "table": { "columns": [ { "key": "event", "label": "Evento" }, { "key": "price_cents", "label": "Preço", "format": "money_cents" } ],
             "rows": [ { "event": "invoice.created", "price_cents": 150 } ] } }

Todas as chaves são opcionais; se a prévia falhar, a página mostra só os argumentos. É uma prévia, não um contrato: o seu backend continua decidindo na hora da chamada.

Widgets

Uma ação de leitura com um bloco widget vira um cartão no painel da área de trabalho no Console, visível para os membros que têm a authorization.action — sem iframe, sem HTML do seu app dentro do Console. Um widget não é oferecido como ferramenta ao Iugu for AI (a resposta é um documento de exibição, o título é o do cartão): mantenha ao lado dele a leitura simples que o assistente vai chamar.

{ "name": "revenue_widget", "title": "Receita", "label": "Faturado, recebido e em aberto nas últimas competências",
  "url": "https://acme.example/api/widgets/revenue", "method": "GET",
  "authorization": { "action": "acme-invoices:reports.read" },
  "widget": { "title": "Receita", "size": { "w": 2, "h": 1 }, "refresh": 300, "link": "https://acme.example/reports" },
  "parameters": [ { "name": "stat", "type": "json", "required": false, "direction": "ParamOutput", "description": "Indicador principal" } ] }

Requisitos: method GET, acr padrão, nenhum parâmetro de entrada (a área de trabalho vem no cabeçalho), size.w 1–4, size.h 1–3, refresh ≥ 60 s, link na mesma origem. O Console chama a ação como a pessoa (cache de 60 s por widget, pessoa e área de trabalho) e renderiza um documento de exibição com os componentes dele:

Chave Forma Aparece como
summary texto uma linha sob o título
stat { "value": "R$ 1.509,70", "label": "Faturado em setembro", "delta": "+12%", "tone": "positive" } o número em destaque
items [{ "label", "value" }] lista rótulo/valor
table { "columns": [{ "key", "label", "format"? }], "rows": [ … ] } tabela com formatos e totais; células status ({ "label", "tone" }) viram etiquetas e link ({ "label", "url" }) viram links
series { "label"?, "format"?, "points": [{ "x": "2026-08", "y": 1509.7 }] } (≤ 60 pontos) gráfico de linha
updated_at ISO 8601 “atualizado há 2 min”

Uma resposta não-2xx, um tempo maior que 5 s ou um corpo inválido aparecem como “‹app› não respondeu”; um 403 do seu backend aparece como “sem permissão”.

Conferir o que o Console aceitou

O Console valida cada ação e exclui as que violam alguma regra (permissão fora das ações implementadas, url em outra origem, dica de exibição inválida, widget com entrada…), mantendo as demais. Para ver o resultado e cada problema:

iugu app actions list --json               # o manifesto como o Console o leu, com `problems`
iugu app actions list --refresh --json     # força uma nova leitura
iugu app actions call list_invoices --json # chama uma ação com o token do próprio app (auxílio de teste)

Pela Lifecycle API: GET https://api.console.iugu.com/v1/apps/{id}/actions[?refresh=true]. Depois de publicado, o app aparece nos assistentes conectados ao Iugu for AI para quem tem a permissão de cada ação — e cada chamada, inclusive de leitura, fica registrada na Auditoria da área de trabalho com os argumentos enviados.

Info

A lista de ferramentas de um assistente é montada quando a conexão começa. Um manifesto que passa a ser aceito (ou muda) durante uma sessão só aparece como ferramenta na próxima conexão — reconecte o assistente (no Claude Desktop, desative e reative o conector). A CLI (iugu call, iugu app actions) lê o manifesto na hora.