Muitto API

Responses API do Muitto

Use modelos de texto do catálogo Muitto com o SDK oficial da OpenAI ou com HTTP direto. A API é stateless, cobra o uso real e expõe somente as capacidades descritas nesta página.

Autenticação

Crie um token pessoal em Configurações → Tokens MCP e envie-o como Bearer. Para gerar, conceda write:generate; para consultar /models, conceda read:catalog. Tokens b2b_ são rejeitados por este endpoint.

Revogue o token na mesma tela de Configurações; chamadas futuras com ele retornam 401.

Base URL

https://muitto.app/api/public/v1

Token pessoal

lv_mcp_YOUR_TOKEN

Primeira requisição

Consulte GET /models primeiro com read:catalog para descobrir IDs de modelos de texto.

curl https://muitto.app/api/public/v1/models \
  -H "Authorization: Bearer lv_mcp_YOUR_TOKEN"

Os IDs são slugs reais do catálogo. Faça GET /models com read:catalog e use um id retornado; não adivinhe nomes de modelos.

curl https://muitto.app/api/public/v1/responses \
  -H "Authorization: Bearer lv_mcp_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: REQUEST_UUID" \
  -d '{"model":"MODEL_FROM_GET_MODELS","input":"Hello from Muitto"}'
import { randomUUID } from "node:crypto";
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MUITTO_TOKEN,
  baseURL: "https://muitto.app/api/public/v1",
  maxRetries: 0,
});

const response = await client.responses.create({
  model: "MODEL_FROM_GET_MODELS",
  instructions: "Be concise.",
  input: "Hello from Muitto",
}, { headers: { "Idempotency-Key": randomUUID() } });
console.log(response.output_text);
import os
import uuid
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MUITTO_TOKEN"],
    base_url="https://muitto.app/api/public/v1",
    max_retries=0,
)
response = client.responses.create(
    model="MODEL_FROM_GET_MODELS",
    input="Hello from Muitto",
    extra_headers={"Idempotency-Key": str(uuid.uuid4())},
)
print(response.output_text)

Entrada e parâmetros

input aceita uma string ou itens de mensagem com conteúdo de texto. instructions vira uma mensagem de sistema no provedor. Imagens, áudio e outros itens multimodais não são aceitos.

{
  "model": "MODEL_FROM_GET_MODELS",
  "instructions": "Optional system guidance",
  "input": "A text string",
  "max_output_tokens": 1024,
  "temperature": 0.2,
  "top_p": 0.9,
  "store": false
}

Os parâmetros aceitos são validados estritamente. Campos desconhecidos retornam erro 400 com uma mensagem explícita.

Function tools
const response = await client.responses.create({
  model: "MODEL_FROM_GET_MODELS",
  input: "Find the order",
  tools: [{
    type: "function",
    name: "lookup_order",
    description: "Find an order by id",
    parameters: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
  }],
}, { headers: { "Idempotency-Key": randomUUID() } });

const calls = response.output.filter((item) => item.type === "function_call");
if (calls.length === 0) throw new Error("The model did not call lookup_order");
const outputs = await Promise.all(calls.map(async (call) => ({
  type: "function_call_output",
  call_id: call.call_id,
  output: JSON.stringify(await lookupOrder(call.arguments)),
})));
// Run every requested function, then send all results in a new stateless request:
const next = await client.responses.create({
  model: "MODEL_FROM_GET_MODELS",
  input: [
    { role: "user", content: "Find the order" },
    ...response.output,
    ...outputs,
  ],
}, { headers: { "Idempotency-Key": randomUUID() } });

As function tools usam o formato de funções do Responses. Built-in tools são rejeitadas. Resultados são enviados como itens function_call_output na próxima requisição stateless.

Saída estruturada
const response = await client.responses.create({
  model: "MODEL_FROM_GET_MODELS",
  input: "Return the status",
  text: {
    format: {
      type: "json_schema",
      name: "status",
      schema: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"], additionalProperties: false },
      strict: true,
    },
  },
}, { headers: { "Idempotency-Key": randomUUID() } });

Use json_schema ou json_object. O modelo e a rota selecionados precisam declarar suporte a saída estruturada.

Streaming buffered

O provedor conclui e a cobrança é liquidada antes de o endpoint emitir o stream. Os eventos são eventos semânticos genuínos do Responses, mas o texto e os argumentos de função são agrupados em deltas completos; isso não é streaming de tokens em tempo real.

const stream = client.responses.stream({
  model: "MODEL_FROM_GET_MODELS",
  input: "Hello",
}, { headers: { "Idempotency-Key": randomUUID() } });
for await (const event of stream) console.log(event.type);
const response = await stream.finalResponse();

A sequência inclui response.created, response.in_progress, eventos de item e parte, deltas de texto ou argumentos e eventos done. response.completed é emitido somente para uma resposta concluída; um limite de saída ou filtro de conteúdo termina em response.incomplete com o motivo correspondente. Cada evento tem ID estável e número de sequência.

Limites e estados

RecursoAceitaRejeita
Entrada e saída de texto✓—
Function tools✓—
Saída estruturada json_schema✓—
Entrada multimodal—✕
previous_response_id / estado da conversa—✕
background / built-in tools—✕
store:true—✕

O corpo da requisição é limitado a 8 MB, a entrada estimada a 2 milhões de tokens e max_output_tokens a 1 milhão. O limite é de 60 requisições por minuto por token e por instância. O stream é emitido depois da conclusão do provedor e da liquidação da cobrança.

Preços e cobrança

A cobrança usa o snapshot USD atual da rota selecionada e o multiplicador de preço do modelo (padrão 4 quando não configurado). Entrada e saída são medidas por 1.000 tokens. Tokens de entrada em cache usam a tarifa de entrada vigente e são reportados separadamente no uso. Cada requisição tem cobrança mínima de US$ 0,001.

Os créditos são reservados antes do envio ao provedor e liquidados com o uso informado após a conclusão. Uso ausente ou resultado upstream incerto deixa a reserva pendente para reconciliação.

Idempotência e erros

O SDK oficial tenta novamente erros 502/503 por padrão e não cria Idempotency-Key automaticamente. Desative os retries automáticos (JavaScript maxRetries: 0; Python max_retries=0) e envie uma chave única na tentativa inicial. Se você repetir manualmente uma tentativa cujo resultado é desconhecido, reutilize a mesma chave: ela retorna 409, não reproduz o corpo nem chama o provedor, e o consumo permanece reservado até a reconciliação.

Entrada inválida retorna 400, token pessoal ausente ou inválido retorna 401, escopo ausente retorna 403, créditos insuficientes retornam 402 e modelo desconhecido retorna 404. Um limite de saída retorna uma resposta incompleta com reason max_output_tokens; um filtro de conteúdo usa reason content_filter. Timeout ou uso ausente mantém os créditos reservados e pendentes.