Inovacc Desenvolvedores

Referência da API

Motor de decisão

Decisões estruturadas por critérios, em um nível de velocidade e precisão a sua escolha.

URL base https://ai.inovacc.dev

Visão geral

O Motor de decisão responde a perguntas estruturadas sobre um conteúdo com vereditos estruturados. Você envia um estado (a coisa a julgar: um texto, um registro, um objeto JSON) e um conjunto de perguntas, cada uma de um tipo conhecido; você recebe de volta uma resposta por pergunta, com o rótulo ou a pontuação escolhidos, as probabilidades por trás deles e uma confiança. O endpoint é POST /v1/run em https://ai.inovacc.dev, chamado em uma rota de decisão da sua organização.

O problema que ele resolve é transformar um julgamento em dados sobre os quais um programa possa agir. Perguntar a um modelo de chat "isto está bom?" devolve prosa que você depois precisa interpretar e que não pode comparar de uma chamada para outra. O Motor de decisão devolve sempre o mesmo formato, então você pode armazená-lo, aplicar limiares e auditá-lo.

Use-o para classificação, pontuação, triagem e verificações contra critérios: este chamado precisa de uma pessoa, qual das cinco categorias se encaixa, quão bem esta resposta atinge o padrão. Quando precisar de prosa gerada, use o Chat com IA; quando o veredito precisar se apoiar nos seus próprios documentos, recupere antes os trechos com Conhecimento e busca vetorial e coloque-os no estado.

Conceitos

Rotas de decisão. Como em todo o Inovacc AI, você chama uma rota, nunca um modelo. GET /v1/models lista as suas rotas; uma entrada cujo endpoint é /v1/run é uma rota de execução, e as que a Inovacc configurou como rotas de decisão respondem no formato abaixo.

Perguntas e seus tipos. questions é um objeto de 1 a 64 entradas, identificadas pelos seus próprios ids (letras, dígitos, _, . ou -, até 100 caracteres). Cada pergunta tem um type, normalmente instructions, e criteria cujo formato é fixado pelo tipo:

TipoPedecriteria
noulsim ou nãoum objeto {"true": "...", "false": "..."}
choiceum rótulo entre váriosum objeto {"label": "description", ...}
scoreum nível em uma escalaum array de níveis, do mais baixo ao mais alto

Os formatos são estritos: uma string de critérios como "1-5" ou "yes|no" é recusada.

Respostas. Cada resposta traz o que o seu tipo produziu: o choice ou o score com probabilities, uma legend e uma confidence. Uma resposta noul traz a sua probabilidade p em vez de uma confiança.

Níveis. Uma rota de decisão atende um de três níveis: fast, standard ou precise. Todos respondem em um único formato, e o engine.tier da resposta diz qual deles respondeu. A confiança não é comparável entre níveis: cada nível informa em sua própria escala, então defina um limiar por nível.

Escalonamento. Com escalate, as perguntas respondidas abaixo de um limiar de confiança são perguntadas de novo, isoladamente, no nível mais forte seguinte (fast, depois standard, depois precise), e a resposta mais forte substitui a mais fraca. Os padrões são fast 0.40, standard 0.80 e precise 0.55; você pode passar min_confidence (um número, ou um por nível) e max_tier. A confiança de uma resposta noul é a distância de p em relação a uma moeda ao ar, |2p - 1|.

Como funciona

Toda chamada leva Authorization: Bearer <key> e um X-Operation-Id. Sua organização vem da chave. O corpo aceita apenas model, input e escalate. Em uma rota de decisão, input é {"state", "questions", "images"?}: state é qualquer valor JSON não nulo, images tem no máximo 16 entradas. Qualquer outro campo dentro de input é recusado com invalid_decision_input, e a resposta nunca o repete.

A resposta é:

{"model": "<route id>", "result": {"answers": {...}, "usage": {"input_tokens", "output_tokens"}}, "state": "Completed", "engine": {"tier", "latency_ms"}}

e seus cabeçalhos trazem x-route-id, x-usage-input-tokens e x-usage-output-tokens.

Com escalonamento, cada resposta também diz qual tier a respondeu e, quando foi escalonada, escalated_from lista os níveis mais baixos e a confiança que cada um deu. Um objeto escalation informa cada tentativa, quantas perguntas foram feitas e quantas voltaram abaixo do limiar, e quantas respostas cada nível deu ao final. Cada tentativa é uma chamada própria, reservada contra as suas cotas e medida no seu próprio nível. Se uma tentativa falha, o escalonamento para, as respostas anteriores são mantidas e a requisição continua sendo 200.

Não há transmissão nem tempo limite de 60 segundos neste endpoint. Uma resposta bem-sucedida é armazenada por 24 horas sob o seu id de operação: o mesmo id devolve a mesma resposta mesclada com x-idempotent-replay: true e não chama nada. O que é medido são tokens, ao preço de cada nível que respondeu.

Primeiros passos

Você precisa de uma chave de API e de uma rota de decisão habilitada para a sua organização (Autenticação).

  1. Encontre a sua rota de decisão com GET /v1/models: uma entrada cujo endpoint é /v1/run.
  2. Faça uma pergunta noul. POST /v1/run com a sua rota como model, um texto curto como state e uma pergunta {"type":"noul","instructions":"...","criteria":{"true":"...","false":"..."}} (veja os exemplos). A resposta traz o id da sua pergunta em result.answers, com a probabilidade p.
  3. Adicione uma pergunta score com três níveis à mesma chamada. As duas respostas voltam em uma só resposta; engine.tier nomeia o nível.
  4. Escalone. Envie a chamada de novo com um novo id de operação e "escalate": true. A resposta agora traz tier por resposta e um objeto escalation listando as tentativas.

Casos de uso

Triagem de chamados. Cada chamado de suporte recebido é o state; uma pergunta choice escolhe a equipe, uma pergunta noul pergunta se uma pessoa precisa responder, uma pergunta score avalia a urgência. As respostas são armazenadas junto com o chamado, e um chamado cuja confiança está abaixo do seu limiar vai para uma pessoa.

Verificações de qualidade em texto gerado. Antes de o rascunho de um assistente ser enviado, uma chamada de decisão o pontua contra o seu padrão ("Abaixo do padrão", "No padrão", "Acima do padrão") e verifica algumas regras noul. Com escalate, apenas os rascunhos sobre os quais o nível rápido ficou em dúvida pagam pelo nível preciso.

Moderação de conteúdo com imagens. O texto de um anúncio e até 16 imagens são julgados contra as suas perguntas de política; a confiança por pergunta decide entre publicar, rejeitar e enviar para revisão.

Limites e preços

LimiteValor
Perguntas por chamada1 a 64
Id da pergunta1 a 100 caracteres: letras, dígitos, _, ., -
Imagens por chamada16
Corpo da requisição1 MiB por padrão; sua organização pode ser configurada entre 1 KiB e 20 MiB
Tokens de entrada por requisiçãoo limite da sua organização
Resposta armazenada para repetição24 horas
Requisições, tokens, custo, simultaneidade, orçamentos diáriosconforme definido para a sua organização

Preços: sob consulta. A unidade de preço é tokens, ao preço do nível que respondeu.

Erros

StatusCódigoO que significa e o que fazer
400invalid_inputinput está ausente ou não é um objeto JSON.
400invalid_decision_inputinput não é {state, questions, images?} ou uma pergunta está malformada; confira tipos e ids.
400invalid_escalateescalate tem uma chave ou valor que não aceita, ou a rota não é uma rota de decisão.
400unsupported_fieldUm campo de nível superior diferente de model, input, escalate.
400model_not_allowed, input_too_largeUse um id de rota válido; encurte o estado.
400missing_operation_id, invalid_operation_idEnvie um X-Operation-Id válido.
401missing_credentials, invalid_credentialsEnvie uma chave válida.
403route_forbiddenA rota não é sua, ou não é uma rota de execução.
409operation_in_progressEsse id de operação ainda está em execução.
413request_too_largeO corpo excede o seu limite de tamanho.
429rate_limited, quota_exceeded, concurrency_limited, budget_exhaustedVeja Erros; aguarde Retry-After quando informado.
502upstream_errorO nível falhou, ou um formato de critérios foi recusado; confira os critérios e tente novamente.
503service_unavailableAs decisões não podem ser alcançadas; tente novamente mais tarde.

Boas práticas

  • Escreva os critérios nos formatos estritos: um objeto para noul e choice, um array para score.
  • Mantenha limiares por nível, nunca um único número para todos os níveis.
  • Use escalate com max_tier para limitar o custo: a maioria das perguntas para no nível mais barato.
  • Coloque no state tudo de que o julgamento precisa, incluindo os trechos recuperados; o motor não vê mais nada.
  • Use ids de pergunta estáveis, para que as respostas possam ser comparadas entre chamadas e armazenadas por id.
  • Derive o id de operação do item julgado para que um lote repetido reaproveite a resposta em vez de pagar duas vezes.

Autenticação

Toda chamada leva a sua chave; a sua organização vem dela. Veja o guia de autenticação.

CabeçalhoAuthorizationBearer <chave de API>
CabeçalhoX-Operation-Idum id único que você escolhe, em toda chamada exceto GET

Endpoints

POST /v1/run

Exemplo

Linguagem

⋮
POST /v1/runExemplo

cURL

curl -X POST "https://ai.inovacc.dev/v1/run" \
  -H "Authorization: Bearer $INOVACC_API_KEY" \
  -H "X-Operation-Id: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer'\''s billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
}'

TypeScript

import crypto from "node:crypto";

const body: Record<string, unknown> = {
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
};

const url = "https://ai.inovacc.dev/v1/run";

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
    "X-Operation-Id": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

console.log(response.status, await response.text());

Python

import json
import os
import urllib.request
import uuid

body = {
    "model": "r_eval",
    "input": {
        "state": "The reply resolves the customer's billing question and links the invoice.",
        "questions": {
            "meets_bar": {
                "type": "score",
                "instructions": "Rate the reply against our support bar.",
                "criteria": [
                    "Below the bar",
                    "At the bar",
                    "Above the bar",
                ],
            },
        },
    },
}

request = urllib.request.Request(
    "https://ai.inovacc.dev/v1/run",
    data=json.dumps(body).encode(),
    method="POST",
    headers={
        "User-Agent": "inovacc-python-sample",
        "Authorization": "Bearer " + os.environ["INOVACC_API_KEY"],
        "X-Operation-Id": str(uuid.uuid4()),
        "Content-Type": "application/json",
    },
)

with urllib.request.urlopen(request) as response:
    print(response.status, response.read().decode())

Go

package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

const url = "https://ai.inovacc.dev/v1/run"

const body = `{
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
}`

// newUUID returns a random (version 4) UUID, from the standard library alone.
func newUUID() string {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	b[6] = b[6]&0x0f | 0x40
	b[8] = b[8]&0x3f | 0x80
	return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
}

func main() {
	req, err := http.NewRequest("POST", url, strings.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INOVACC_API_KEY"))
	req.Header.Set("X-Operation-Id", newUUID())
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Status, string(out))
}

Rust

// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// Cargo.toml: serde_json = "1"
// Cargo.toml: uuid = { version = "1", features = ["v4"] }

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("INOVACC_API_KEY")?;
    let body = serde_json::json!({
        "model": "r_eval",
        "input": {
            "state": "The reply resolves the customer's billing question and links the invoice.",
            "questions": {
                "meets_bar": {
                    "type": "score",
                    "instructions": "Rate the reply against our support bar.",
                    "criteria": [
                        "Below the bar",
                        "At the bar",
                        "Above the bar"
                    ]
                }
            }
        }
    });
    let response = reqwest::blocking::Client::new()
        .post("https://ai.inovacc.dev/v1/run")
        .bearer_auth(api_key)
        .header("X-Operation-Id", uuid::Uuid::new_v4().to_string())
        .json(&body)
        .send()?;
    println!("{} {}", response.status(), response.text()?);
    Ok(())
}

JavaScript

import crypto from "node:crypto";

const body = {
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
};

const url = "https://ai.inovacc.dev/v1/run";

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INOVACC_API_KEY}`,
    "X-Operation-Id": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

console.log(response.status, await response.text());

PHP

<?php

$body = <<<'JSON'
{
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
}
JSON;

function uuid4(): string
{
    $bytes = random_bytes(16);
    $bytes[6] = chr(ord($bytes[6]) & 0x0f | 0x40);
    $bytes[8] = chr(ord($bytes[8]) & 0x3f | 0x80);
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
}

$curl = curl_init('https://ai.inovacc.dev/v1/run');
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('INOVACC_API_KEY'),
        'X-Operation-Id: ' . uuid4(),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $body,
]);

$response = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_HTTP_CODE), ' ', $response, "\n";
curl_close($curl);

Ruby

require "net/http"
require "securerandom"
require "uri"

uri = URI('https://ai.inovacc.dev/v1/run')
body = <<~'JSON'
{
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
}
JSON

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch('INOVACC_API_KEY')}"
request["X-Operation-Id"] = SecureRandom.uuid
request["Content-Type"] = "application/json"
request.body = body

response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts "#{response.code} #{response.body}"

Java

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.UUID;

public class Main {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("INOVACC_API_KEY");
        String body = "{" +
                "\"model\": \"r_eval\"," +
                "\"input\": {" +
                "\"state\": \"The reply resolves the customer's billing question and links the invoice.\"," +
                "\"questions\": {" +
                "\"meets_bar\": {" +
                "\"type\": \"score\"," +
                "\"instructions\": \"Rate the reply against our support bar.\"," +
                "\"criteria\": [" +
                "\"Below the bar\"," +
                "\"At the bar\"," +
                "\"Above the bar\"" +
                "]" +
                "}" +
                "}" +
                "}" +
                "}";

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://ai.inovacc.dev/v1/run"))
                .header("Authorization", "Bearer " + apiKey)
                .header("X-Operation-Id", UUID.randomUUID().toString())
                .header("Content-Type", "application/json")
                .method("POST", HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
                .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}

C#

using System.Text;

var body = "{" +
    "\"model\": \"r_eval\"," +
    "\"input\": {" +
    "\"state\": \"The reply resolves the customer's billing question and links the invoice.\"," +
    "\"questions\": {" +
    "\"meets_bar\": {" +
    "\"type\": \"score\"," +
    "\"instructions\": \"Rate the reply against our support bar.\"," +
    "\"criteria\": [" +
    "\"Below the bar\"," +
    "\"At the bar\"," +
    "\"Above the bar\"" +
    "]" +
    "}" +
    "}" +
    "}" +
    "}";

var url = "https://ai.inovacc.dev/v1/run";
var apiKey = Environment.GetEnvironmentVariable("INOVACC_API_KEY");

using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("X-Operation-Id", Guid.NewGuid().ToString());
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");

Kotlin

import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.util.UUID

fun main() {
    val apiKey = System.getenv("INOVACC_API_KEY")
    val body = "{" +
        "\"model\": \"r_eval\"," +
        "\"input\": {" +
        "\"state\": \"The reply resolves the customer's billing question and links the invoice.\"," +
        "\"questions\": {" +
        "\"meets_bar\": {" +
        "\"type\": \"score\"," +
        "\"instructions\": \"Rate the reply against our support bar.\"," +
        "\"criteria\": [" +
        "\"Below the bar\"," +
        "\"At the bar\"," +
        "\"Above the bar\"" +
        "]" +
        "}" +
        "}" +
        "}" +
        "}"

    val request = HttpRequest.newBuilder()
        .uri(URI.create("https://ai.inovacc.dev/v1/run"))
        .header("Authorization", "Bearer " + apiKey)
        .header("X-Operation-Id", UUID.randomUUID().toString())
        .header("Content-Type", "application/json")
        .method("POST", HttpRequest.BodyPublishers.ofString(body))
        .build()

    val response = HttpClient.newHttpClient()
        .send(request, HttpResponse.BodyHandlers.ofString())
    println("${response.statusCode()} ${response.body()}")
}

Swift

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

let body = #"""
{
  "model": "r_eval",
  "input": {
    "state": "The reply resolves the customer's billing question and links the invoice.",
    "questions": {
      "meets_bar": {
        "type": "score",
        "instructions": "Rate the reply against our support bar.",
        "criteria": [
          "Below the bar",
          "At the bar",
          "Above the bar"
        ]
      }
    }
  }
}
"""#

let apiKey = ProcessInfo.processInfo.environment["INOVACC_API_KEY"] ?? ""

let url = URL(string: "https://ai.inovacc.dev/v1/run")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue(UUID().uuidString, forHTTPHeaderField: "X-Operation-Id")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(body.utf8)

let (data, response) = try await URLSession.shared.data(for: request)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
print(status, String(decoding: data, as: UTF8.self))
Detalhes da fonte

Entrada do catálogo

Id
identity/component-taxonomy/capabilities#decision
Repositório
identity
Caminho
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82

Exemplo

Id
identity/component-taxonomy/quickstart#decision
Repositório
identity
Caminho
contracts/component-taxonomy/catalog/capabilities.json
Commit
08ef3cd75d82
Atualizado em 2026-10-10.