API para usuários
A API v1 permite criar estudos Fast Track, importar estudos por ZIP, consultar o preparo do estudo e disparar execuções pelo Portal Myria.
Todos os endpoints ficam sob /api/v1/, retornam JSON e acessam apenas os dados da organização vinculada à chave de API. Requisições com corpo usam JSON, exceto o upload de ZIP, que usa multipart/form-data.
Última atualização desta página: 2026-06-24.
Como conseguir uma chave
No Portal, acesse Perfil → Chaves de API.
Crie uma chave com os escopos necessários:
| Escopo | Permite |
|---|---|
studies:write | Criar estudos Fast Track, enviar ZIPs, criar importações, adicionar RV+/M+ e alterar opções de rodadas. |
studies:read | Consultar importações e estudos. |
executions:write | Solicitar execuções. |
executions:read | Consultar execuções. |
O segredo completo aparece apenas uma vez, no momento da criação. Guarde-o em local seguro.
Se você não preencher Expiração, a chave vence automaticamente em 90 dias.
Quando a data é informada, a expiração ocorre no fim do dia escolhido. Depois,
você pode revogar a chave em Perfil → Chaves de API.
Formato da chave:
myria_live_<identificador>.<segredo>
Use a chave em todas as requisições:
Authorization: Bearer myria_live_<identificador>.<segredo>
Chaves expiradas, revogadas ou inválidas retornam 401. Chaves válidas sem escopo suficiente retornam 403. Muitas falhas de autenticação para o mesmo IP ou prefixo de chave retornam 429 rate_limited.
Limites de chamadas
Criações Fast Track, uploads, operações incrementais e execuções usam rate limit por chave e por organização. Os defaults são:
| Bucket | Criação de estudos | Uploads | Operações incrementais | Execuções |
|---|---|---|---|---|
| Chave | 30/hour | 30/hour | 60/hour | 60/hour |
| Organização | 300/hour | 300/hour | 600/hour | 600/hour |
Quando o limite é excedido, a resposta é 429 rate_limited com
Retry-After. Replays idempotentes de recursos já criados retornam o recurso
existente sem consumir novo slot de escrita.
Os buckets usam env vars no formato <numero>/<janela>. Os nomes são
MYRIA_API_V1_RATE_LIMIT_KEY_STUDIES,
MYRIA_API_V1_RATE_LIMIT_ORG_STUDIES,
MYRIA_API_V1_RATE_LIMIT_KEY_UPLOADS,
MYRIA_API_V1_RATE_LIMIT_ORG_UPLOADS,
MYRIA_API_V1_RATE_LIMIT_KEY_INCREMENTALS,
MYRIA_API_V1_RATE_LIMIT_ORG_INCREMENTALS,
MYRIA_API_V1_RATE_LIMIT_KEY_EXECUTIONS e
MYRIA_API_V1_RATE_LIMIT_ORG_EXECUTIONS.
Fluxo recomendado
- Para Novo Estudo operacional, chame
POST /api/v1/studiescomtemplate: "fast_track". Para importar deck próprio, envie o ZIP emPOST /api/v1/studies/imports. - Na criação Fast Track, guarde o
creation_ide ostudy_id. Na importação ZIP, guarde oimport_id. - Se importou ZIP, consulte
GET /api/v1/studies/imports/{import_id}até receber umstudy_id. - Consulte
GET /api/v1/studies/{study_id}atéready_for_executionsertrue. - Para adicionar rodadas, chame
POST /api/v1/studies/{study_id}/incremental-operationscommode: "rv"oumode: "m"e consulteGET /api/v1/studies/{study_id}/incremental-operations/{operation_id}. - Dispare
POST /api/v1/studies/{study_id}/executions. - Consulte
GET /api/v1/studies/{study_id}/executions/{execution_id}até a execução terminar.
Receita ponta a ponta sem usar a UI
- Crie o Fast Track com
POST /api/v1/studies, escopostudies:write,Content-Type: application/jsone body{"template": "fast_track"}. UseIdempotency-Keye guardecreation_id,study_idestudy_status_url. - Consulte
GET /api/v1/studies/{study_id}comstudies:readatéready_for_executionficartrueou o status chegar ablocked/failed. - Adicione RV+ com
POST /api/v1/studies/{study_id}/incremental-operationse body{"mode": "rv", "count": 1}. Adicione M+ com{"mode": "m"}. Ocountpode ir de1a24; RV+ na última RV do mês pode cruzar para M+ quando a regra operacional pedir isso. - Aguarde a preparação em
GET /api/v1/studies/{study_id}/incremental-operations/{operation_id}. Emcompleted, o camporodadastraz as rodadas criadas no mesmo formato deGET /api/v1/studies/{study_id}. - Ajuste corte, PREVS, binários e GEVAZP com o PATCH existente
PATCH /api/v1/studies/{study_id}/rodadas/{rodada_id_or_name}/options. Ele aceitacorte,prevs_file,prevs_source,decomp_base,decomp_base_earm_column,binario_id,gevazp.binario_idegevazp.run_gevazp. - Execute o estudo inteiro com
POST /api/v1/studies/{study_id}/executions. Execução parcial, resume, reset, reexecução e prioridade explícita ainda retornam409 unsupported_execution_mode. - Consulte
GET /api/v1/studies/{study_id}/executions/{execution_id}até status terminal. Userequested_rodadaserun_id_snapshotpara saber quais rodadas pertencem à execução. - Leia o resultado com
GET /api/v1/studies/{study_id}/executions/{execution_id}/rodadas/{rodada_id_or_name}/outputs/deckoutput.json. O output só é retornado quando a execução está terminal, a rodada pertence ao snapshot e orun_idatual bate com orun_idregistrado pela execução.
Continuam iguais nesta versão: importação ZIP, consulta de estudo, PATCH de
rodada_options, execução de estudo inteiro, listagem e detalhe de execuções e
leitura de deckoutput.json. Outputs arbitrários, criação genérica por YAML,
execução parcial, resume e reset seguem fora do contrato público v1.
Idempotência
Chamadas mutáveis aceitam o cabeçalho Idempotency-Key: criações Fast Track,
importações, operações incrementais, execuções e o PATCH de opções de rodada.
Idempotency-Key: cliente-123-importacao-001
Use esse cabeçalho para repetir uma chamada com segurança em caso de timeout ou falha de rede. Repetir a mesma chamada com a mesma chave retorna o mesmo recurso, mesmo se o limite de chamadas já tiver sido excedido, e não consome novo slot de escrita.
Para criações Fast Track e importações, a chave de idempotência é isolada por
organização e usuário dono da chave de API. Para operações incrementais e
execuções, ela é isolada por estudo e usuário. Para PATCH de opções, ela é
isolada por estudo, rodada e usuário. Dois usuários da mesma organização podem
usar o mesmo valor sem
colisão, e o mesmo usuário pode reutilizar o valor em estudos ou rodadas
diferentes. Reusar a mesma chave com payload ou arquivo diferente dentro do
mesmo escopo retorna
409 idempotency_conflict.
Endpoints
POST /api/v1/studies
Cria um estudo operacional equivalente ao botão Novo Estudo do Portal usando o template Fast Track.
Escopo exigido: studies:write.
Request:
POST /api/v1/studies
Authorization: Bearer <chave>
Idempotency-Key: create-fast-track-20260609-001
Content-Type: application/json
{
"template": "fast_track",
"title": "Fast Track Junho 2026",
"org_slug": "minha-org"
}
Campos:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
template | texto | sim | Nesta versão, apenas "fast_track". |
title | texto | não | Título sugerido. Se omitido, a API usa o título descoberto pelo Fast Track e garante unicidade. Em ambientes com paths legados, o valor também precisa ser válido como raiz de path Gitea; paths absolutos, traversal, segmentos vazios e barras invertidas retornam 400 invalid_payload. |
org_slug | texto | não | Se enviado, deve ser igual à organização da chave. |
Resposta 202:
{
"success": true,
"creation_id": "create_6d0f1b7a8f8d4b70b5e9d4d8437628ef",
"study_id": 42,
"status": "created",
"template": "fast_track",
"options": {
"title": "Fast Track Junho 2026"
},
"study": {
"id": 42,
"title": "Fast Track Junho 2026",
"org_slug": "minha-org",
"status": "materializing",
"ready_for_execution": false,
"rodadas": [
{
"id": 101,
"name": "DC202606-sem1",
"type": "decomp",
"ready_for_execution": false
}
]
},
"study_status_url": "/api/v1/studies/42",
"errors": [],
"created_at": "2026-06-09T12:00:00Z",
"updated_at": "2026-06-09T12:00:00Z"
}
O campo study usa o mesmo formato de GET /api/v1/studies/{study_id}.
Durante a materialização, consulte study_status_url até o estudo ficar pronto
ou bloqueado. Templates desconhecidos retornam 400 invalid_template. Falhas na
descoberta Fast Track retornam 503 fast_track_unavailable sem criar estudo
parcial e sem expor paths ou mensagens internas em error.details.reason. Se o
estudo já foi criado e o dispatch de materialização falhar após o commit, a
resposta continua sendo o recurso de criação com status: "created";
o cliente deve consultar study.status e study.materialization.status, que
podem indicar failed com mensagem pública de materialização.
POST /api/v1/studies/imports
Cria uma importação assíncrona de estudo a partir de um ZIP.
Escopo exigido: studies:write.
O arquivo deve ter extensão .zip, assinatura ZIP válida e respeitar o limite
configurado no Portal. O default é 134217728 bytes comprimidos. O servidor não
usa o MIME informado no multipart como critério de rejeição, e rejeita ZIPs com
metodos de compressao nao suportados, paths absolutos, traversal ..,
caracteres de controle em nomes de entrada, entradas criptografadas, colisões
case-insensitive ou conflitos entre arquivo e diretório. Entradas operacionais
de macOS, como __MACOSX, .DS_Store e nomes
iniciados por ._, são ignoradas antes da validação recursiva; ZIPs aninhados
reais continuam sendo validados. Na prática, um resource fork como
__MACOSX/estudo/._NW202602.zip não torna o upload inválido, mas um arquivo
.zip real fora de metadata ignorada ainda é aberto e validado.
Request:
POST /api/v1/studies/imports
Authorization: Bearer <chave>
Idempotency-Key: import-20260603-001
Content-Type: multipart/form-data
Campos:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
file | arquivo .zip | sim | ZIP do estudo. |
title | texto | não | Título sugerido para o estudo. |
titulo | texto | não | Alias aceito para title. |
org_slug | texto | não | Se enviado, deve ser igual à organização da chave. |
selected_rodadas | lista, JSON ou texto | não | Subconjunto de rodadas a importar. Aceita campo repetido, selected_rodadas[], array JSON string ou lista separada por vírgulas. |
execution_options | JSON object | não | Reservado para opções futuras. Campos internos ainda não são aceitos. |
rodada_options | JSON object | não | Opcoes por rodada da Fase 1. A API aplica o bloco antes do readiness e devolve options.rodada_options normalizado. |
Exemplo com rodada_options:
curl -sS -X POST "$MYRIA_URL/api/v1/studies/imports" \
-H "Authorization: Bearer $MYRIA_API_KEY" \
-H "Idempotency-Key: import-20260608-rodada-options-001" \
-F "file=@deck.zip;type=application/zip" \
-F "title=DECOMP PMO 202606 RV0" \
-F "selected_rodadas[]=decomp_rv0" \
-F 'rodada_options={"decomp_rv0":{"corte":{"gitea_path":"estudos/estudo_120/rodada_991","gitea_repo":"estudos","org_slug":"minha-org"},"prevs_file":"prevs.rv0"}}'
Resposta 202:
{
"success": true,
"import_id": "imp_6d0f1b7a8f8d4b70b5e9d4d8437628ef",
"study_id": null,
"status": "uploaded",
"retryable": true,
"message": "ZIP recebido para processamento.",
"file": {
"sha256": "b1946ac92492d2347c6235b4d2611184",
"size_bytes": 123456
},
"options": {
"title": "DECOMP PMO 202606 RV0",
"selected_rodadas": [
"decomp_rv0"
],
"execution_options": {},
"rodada_options": {
"decomp_rv0": {
"corte": {
"org_slug": "minha-org",
"gitea_repo": "estudos",
"gitea_path": "estudos/estudo_120/rodada_991"
},
"prevs_file": "prevs.rv0"
}
}
},
"errors": [],
"study_status_url": null,
"import_status_url": "/api/v1/studies/imports/imp_6d0f1b7a8f8d4b70b5e9d4d8437628ef",
"created_at": "2026-06-03T12:00:00Z",
"updated_at": "2026-06-03T12:00:00Z",
"started_at": null,
"finished_at": null
}
O payload de resposta inclui options.rodada_options normalizado.
GET /api/v1/studies/imports/{import_id}
Consulta o status da importação.
Escopo exigido: studies:read.
Estados possíveis:
| Status | Significado |
|---|---|
uploaded | ZIP recebido e aguardando processamento. |
parsing | ZIP em leitura/normalização. |
materializing | Arquivos sendo gravados no Gitea. |
validating | Readiness sendo calculado. |
ready | Estudo pronto para execução. |
blocked | Estudo criado com pendências bloqueantes. |
failed | Falha na importação ou validação. |
O campo errors usa códigos e mensagens públicas estáveis. Ele não inclui
tracebacks, caminhos internos, mensagens de broker ou detalhes de backend.
Quando study_id estiver preenchido, use study_status_url para consultar o estudo.
O campo retryable é true para jobs ainda em uploaded e para falhas de
despacho Celery (dispatch_failed). Falhas de parse, materialização ou
readiness exigem nova importação ou correção operacional.
GET /api/v1/studies/{study_id}
Consulta um estudo.
Escopo exigido: studies:read.
Resposta:
{
"success": true,
"study": {
"id": 42,
"title": "DECOMP PMO 202606 RV0",
"org_slug": "minha-org",
"status": "ready",
"ready_for_execution": true,
"origin": "importado",
"gitea_repo": "estudos",
"gitea_path": "estudo_42",
"organization": {
"id": 3,
"slug": "minha-org"
},
"validation": {
"status": "valid",
"raw_status": "completed",
"is_valid": true,
"errors": []
},
"materialization": {
"id": 77,
"kind": "zip_import",
"status": "completed",
"completed": true,
"retryable": false,
"gitea_org": "org-minha-org",
"gitea_repo": "estudos",
"gitea_path": "estudo_42",
"path_scope": "user_study",
"error_message": "",
"metadata": {},
"created_at": "2026-06-03T11:59:00Z",
"updated_at": "2026-06-03T12:00:00Z"
},
"rodadas": [
{
"id": 434,
"name": "decomp_rv0",
"order": 0,
"type": "decomp",
"rv": "rv0",
"status": "pending",
"ready_for_execution": true,
"readiness_status": "ready",
"readiness": {},
"validation_status": "valid",
"validation": {
"is_valid": true,
"errors": []
},
"execution_status": "idle",
"effective_execution_options": {},
"run_id": null,
"dispatch_state": null,
"execution_steps": {},
"errors": [],
"updated_at": "2026-06-03T12:00:00Z"
}
],
"created_at": "2026-06-03T12:00:00Z",
"updated_at": "2026-06-03T12:00:00Z"
}
}
Estados consolidados do estudo:
| Status | Significado |
|---|---|
materializing | Arquivos ainda estão sendo gravados. |
validating | Validação/readiness ainda está em andamento. |
ready | Estudo pronto para execução. |
blocked | Há pendências bloqueantes. |
failed | Houve falha operacional ou validação falhou. |
O status ready usa a mesma regra de elegibilidade da execução: pendências que
dependem apenas de rodadas internas da cadeia não bloqueiam o disparo quando há
uma rodada inicial pronta. Readiness unknown permanece como validating na
API.
Mensagens em validation.errors são mensagens públicas; falhas internas de
worker, traceback, paths locais e detalhes de backend são substituídos por uma
mensagem genérica.
POST /api/v1/studies/{study_id}/incremental-operations
Cria uma operação incremental assíncrona para adicionar RV+ ou M+ a um estudo operacional.
Escopo exigido: studies:write.
Request:
POST /api/v1/studies/42/incremental-operations
Authorization: Bearer <chave>
Idempotency-Key: incr-20260609-001
Content-Type: application/json
{
"mode": "rv",
"count": 1
}
Campos:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
mode | texto | sim | "rv" para RV+ ou "m" para M+. |
count | inteiro | não | Quantidade de incrementos, entre 1 e 24. Default: 1. |
Resposta 202:
{
"success": true,
"operation_id": "incr_6d0f1b7a8f8d4b70b5e9d4d8437628ef",
"study_id": 42,
"status": "pending",
"mode": "rv",
"count": 1,
"created": 0,
"rodada_ids": [],
"rodadas": [],
"options": {
"mode": "rv",
"count": 1
},
"error": null,
"study_status_url": "/api/v1/studies/42",
"operation_status_url": "/api/v1/studies/42/incremental-operations/incr_6d0f1b7a8f8d4b70b5e9d4d8437628ef",
"created_at": "2026-06-09T12:00:00Z",
"updated_at": "2026-06-09T12:00:00Z",
"started_at": null,
"finished_at": null
}
Se o broker não retornar task_id ou o dispatch levantar exceção depois de a
operação ser persistida, o POST ainda retorna 202 com o próprio recurso da
operação: success: true, status: "failed" e error.code: "incremental_operation_failed".
O GET posterior retorna o mesmo formato com HTTP 200. Conflitos de execução,
materialização ativa, operação incremental
ativa, deck compartilhado ou recurso de outra organização retornam erros
públicos sem traceback.
GET /api/v1/studies/{study_id}/incremental-operations/{operation_id}
Consulta uma operação incremental pelo operation_id retornado no POST.
Escopo exigido: studies:read.
A resposta usa o mesmo shape do POST. Quando a operação criou rodadas, o campo
rodadas usa o mesmo formato das rodadas de GET /api/v1/studies/{study_id}.
Status possíveis da operação:
| Status | Significado |
|---|---|
pending | Operação aceita e aguardando preparação no worker. |
running | Rodadas incrementais foram criadas e estão em montagem/materialização. |
completed | Preparação e montagem terminaram. |
failed | Preparação, dispatch ou montagem falhou; consulte error. |
cancelled | Operação cancelada por rotina operacional. |
PATCH /api/v1/studies/{study_id}/rodadas/{rodada_id_or_name}/options
PATCH /api/v1/studies/{study_id}/rodadas/{rodada_id_or_name}/options
Atualiza as opcoes de uma rodada de um estudo ja materializado.
Escopo exigido: studies:write.
O body JSON deve conter apenas os campos da rodada. O endpoint resolve
{rodada_id_or_name} por id ou nome, retorna 400 unknown_rodada quando nao
consegue resolver a rodada, 409 ambiguous_rodada quando o nome e ambiguo e
409 study_conflict quando o estudo esta compartilhado ou em mutacao ativa.
As mesmas regras de allow-list da API v1 se aplicam aqui: corte, PREVS, base DECOMP, binarios e flag GEVAZP sao aplicados dentro de uma transacao, antes de recalcular readiness e sincronizar o YAML do estudo.
Campos de Fase 1 aceitos neste PATCH:
corteprevs_fileprevs_sourcedecomp_basedecomp_base_earm_columnbinario_idgevazp.binario_idgevazp.run_gevazp
prevs_file deve ser apenas o nome do arquivo PREVS, sem caminho e sem
metacaracteres de glob. Formatos aceitos incluem prevs.rv0,
prevs-2000.rv0 e variantes seguras com letras, números, _, - ou .
antes do sufixo .rvN.
Exemplo:
curl -sS -X PATCH "$MYRIA_URL/api/v1/studies/$STUDY_ID/rodadas/decomp_rv0/options" \
-H "Authorization: Bearer $MYRIA_API_KEY" \
-H "Idempotency-Key: patch-20260608-rodada-options-001" \
-H "Content-Type: application/json" \
-d '{
"corte": {
"gitea_path": "estudos/estudo_120/rodada_991",
"gitea_repo": "estudos",
"org_slug": "minha-org"
},
"prevs_file": "prevs.rv0"
}'
Resposta: o mesmo shape de GET /api/v1/studies/{study_id} com as rodadas
atualizadas.
Alterando o EARM de entrada de uma rodada DECOMP
Para executar um DECOMP importado sem rematerializar o deck com monta_rv0 ou
monta_rvx, mas usando EARM inicial vindo de outro deck, configure
rodada_options.<rodada>.decomp_base. Esse campo aponta para o deck DECOMP que o
preflight deve usar como fonte de EARM.
Durante o preflight, a API procura sumario.* no decomp_base; se nao houver
EARM por usina no sumario, tenta relato.*. Em seguida, atualiza apenas o campo
VINI das linhas UH ja existentes no dadger da rodada que sera executada. O
bloco UH nao e copiado integralmente do deck fonte.
Por padrao, o preflight mantem a regra operacional atual: para rv0, usa a
penultima coluna numerica do sumario; para rv1+, usa Sem_01. No fallback
por relato, usa o valor de fim da primeira semana. Para forcar o EARM inicial
do deck fonte, envie decomp_base_earm_column: "inic" junto com decomp_base;
nesse caso a API usa a coluna Inic. do sumario ou a coluna inicial da tabela
de volume por usina no relato.
Exemplo aplicando a mudanca em um estudo ja importado:
curl -sS -X PATCH "$MYRIA_URL/api/v1/studies/$STUDY_ID/rodadas/decomp_rv0/options" \
-H "Authorization: Bearer $MYRIA_API_KEY" \
-H "Idempotency-Key: patch-earm-decomp-base-001" \
-H "Content-Type: application/json" \
-d '{
"decomp_base": {
"gitea_path": "estudos/estudo_120/rodada_991",
"gitea_repo": "estudos",
"org_slug": "minha-org"
},
"decomp_base_earm_column": "inic"
}'
O mesmo bloco pode ser enviado na importacao do ZIP:
curl -sS -X POST "$MYRIA_URL/api/v1/studies/imports" \
-H "Authorization: Bearer $MYRIA_API_KEY" \
-H "Idempotency-Key: import-earm-decomp-base-001" \
-F "file=@deck.zip;type=application/zip" \
-F "title=Estudo com EARM ajustado" \
-F 'rodada_options={
"decomp_rv0": {
"decomp_base": {
"gitea_path": "estudos/estudo_120/rodada_991",
"gitea_repo": "estudos",
"org_slug": "minha-org"
},
"decomp_base_earm_column": "inic"
}
}'
Tambem e possivel enviar decomp_base no rodada_options do POST de execucao,
desde que isso seja feito antes do dispatch da rodada:
{
"rodada_options": {
"decomp_rv0": {
"decomp_base": {
"gitea_path": "estudos/estudo_120/rodada_991",
"gitea_repo": "estudos",
"org_slug": "minha-org"
},
"decomp_base_earm_column": "inic"
}
}
}
Use rodada apenas quando quiser apontar para outra rodada do mesmo estudo. Para
forcar um path especifico no Gitea, envie gitea_path sem rodada; quando
rodada e informado, a API resolve o path a partir da rodada referenciada.
Observacoes:
decomp_baseso se aplica a rodadasdecomp.decomp_base_earm_columnaceita apenas"inic"ounull; campo ausente preserva a regra operacional atual.- O deck fonte precisa conter
sumario.*ourelato.*com EARM por usina. - Se uma UHE do
dadgeralvo nao existir na fonte, o valor atual dessa linha e mantido. - Regras de preflight (
preflight_rulesouregras_volume_uhe.dat) rodam depois desse ajuste e podem sobrescrever volumesUHespecificos.
POST /api/v1/studies/{study_id}/executions
Solicita execução do estudo.
Escopo exigido: executions:write.
Na versão atual, a API executa o estudo inteiro. Execução parcial, reexecução, resume e reset ainda não são suportados pela API v1.
Se você enviar rodadas, requested_rodadas, action, mode ou priority_main_runs, a API retorna 409 unsupported_execution_mode.
A execução exige estudo válido, readiness suficiente para iniciar a cadeia e
capacidade operacional: ao menos um servidor ativo na organização ou, no
rollout OCI atual, overflow OCI elegível para a organização: limite OCI da
organização com max_dynamic_ocpus > 0, decomp_ocpus/newave_ocpus válidos,
credenciais OCI
globais completas e descriptografáveis em ConfigGeral, shapes e memória OCI
globais válidos, ao menos um placement regional global habilitado e válido,
MYRIA_OCI_DYNAMIC_ENABLED=true e MYRIA_OCI_PROVIDER_DRY_RUN=false. Um
placement regional global válido inclui região, compartment, availability
domain, subnet, imagem, usuário/chave SSH e NSG quando houver IP público.
Pendências puramente encadeadas entre rodadas internas não bloqueiam quando há
uma rodada inicial pronta; sensibilidades com pendências bloqueantes impedem o
disparo pela API v1.
rodada_options e aplicado dentro da transacao antes do readiness e antes do
dispatch. A mutacao de Rodada.config, Rodada.binario e Estudo.yaml_content
e permanente quando a execucao e aceita. Se a aplicacao das opcoes falhar,
nenhuma StudyExecutionRequest e criada. Se as opcoes forem aplicadas mas o
inicio da execucao falhar, a request fica failed e as mutacoes das opcoes sao
restauradas.
Opções aceitas no body:
| Campo | Tipo | Descrição |
|---|---|---|
org_slug | texto | Opcional. Se enviado, deve ser igual à organização da chave; divergência retorna 403 organization_mismatch. |
execution_options.decomp_cpu_cores | inteiro ou null | Cores a reservar para cada rodada DECOMP. Alias: processors. Valor explícito exige capacidade fixa conhecida ou overflow OCI elegível para a organização; o limite validado é o maior valor entre a capacidade fixa conhecida e o decomp_ocpus da organização. Se a execução cair em OCI dinâmica, o MVP usa o decomp_ocpus da organização e ignora esse override por estudo. |
execution_options.decomp_max_inviab_attempts | inteiro 1..20 ou null | Máximo de execuções internas do tratamento de inviabilidades DECOMP. Alias: infeasibility_treatment_attempts. |
rodada_options | objeto | Opcoes por rodada da Fase 1. A API aplica o bloco antes do readiness e devolve options.rodada_options normalizado. |
null limpa a configuração do estudo. Sem valor explícito, o DECOMP usa o default operacional atual.
Request:
POST /api/v1/studies/42/executions
Authorization: Bearer <chave>
Idempotency-Key: exec-20260603-001
Content-Type: application/json
Body:
{
"execution_options": {
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7
},
"rodada_options": {
"decomp_rv0": {
"prevs_file": "prevs.rv0"
}
}
}
Resposta 202:
{
"success": true,
"execution_id": "exec_73fd5b985c6a4b629e2e5411bbd4bd12",
"study_id": 42,
"status": "running",
"requested_rodadas": [
{
"id": 434,
"name": "decomp_rv0",
"order": 0,
"type": "decomp"
}
],
"rodadas": [
{
"id": 434,
"name": "decomp_rv0",
"order": 0,
"type": "decomp",
"status": "running",
"execution_status": "running",
"run_id": "a1b2c3d4",
"dispatch_state": "started",
"execution_steps": {
"Execucao": "running"
},
"server": {
"id": 7,
"name": "srv-prod-1"
},
"effective_execution_options": {
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7
},
"errors": [],
"updated_at": "2026-06-03T12:05:02Z"
}
],
"options": {
"execution_options": {
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7
},
"rodada_options": {
"decomp_rv0": {
"prevs_file": "prevs.rv0"
}
}
},
"run_id_snapshot": {
"434": {
"rodada_id": 434,
"name": "decomp_rv0",
"status": "running",
"run_id": "a1b2c3d4",
"dispatch_state": "started",
"output": {
"name": "deckoutput.json",
"gitea_repo": "estudos",
"gitea_path": "estudo_42/decomp_rv0/deckoutput.json",
"ref": "main"
}
}
},
"errors": [],
"execution_status_url": "/api/v1/studies/42/executions/exec_73fd5b985c6a4b629e2e5411bbd4bd12",
"created_at": "2026-06-03T12:05:00Z",
"updated_at": "2026-06-03T12:05:02Z",
"started_at": "2026-06-03T12:05:02Z",
"finished_at": null
}
Se a execução com chave de idempotência tiver sido criada, mas ainda não tiver
enfileirado rodadas, uma nova chamada com o mesmo Idempotency-Key tenta
retomar o start antes de retornar o replay. Se as rodadas já tiverem sido
enfileiradas, mas o snapshot da execução ainda não tiver sido gravado, o replay
reconstrói requested_rodadas e run_id_snapshot a partir das rodadas atuais e
retorna o recurso existente. Se o start falhar antes de enfileirar, o recurso
final pode retornar status: "failed" com requested_rodadas: [], rodadas: []
e run_id_snapshot: {}.
GET /api/v1/studies/{study_id}/executions
Lista execuções de um estudo em ordem decrescente de criação.
Escopo exigido: executions:read.
Parâmetros de query:
| Parâmetro | Default | Descrição |
|---|---|---|
limit | 50 | Quantidade máxima de execuções retornadas. Aceita 1..100. |
offset | 0 | Quantidade de execuções a pular. Deve ser maior ou igual a zero. |
Resposta:
{
"success": true,
"executions": [
{
"success": true,
"execution_id": "exec_73fd5b985c6a4b629e2e5411bbd4bd12",
"study_id": 42,
"status": "running",
"requested_rodadas": [
{
"id": 434,
"name": "decomp_rv0",
"order": 0,
"type": "decomp"
}
],
"rodadas": [
{
"id": 434,
"name": "decomp_rv0",
"order": 0,
"type": "decomp",
"status": "running",
"execution_status": "running",
"run_id": "a1b2c3d4",
"dispatch_state": "started",
"execution_steps": {
"Execucao": "running"
},
"server": {
"id": 7,
"name": "srv-prod-1"
},
"effective_execution_options": {
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7
},
"errors": [],
"updated_at": "2026-06-03T12:05:02Z"
}
],
"options": {
"execution_options": {
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7
},
"rodada_options": {
"decomp_rv0": {
"prevs_file": "prevs.rv0"
}
}
},
"run_id_snapshot": {
"434": {
"rodada_id": 434,
"name": "decomp_rv0",
"status": "running",
"run_id": "a1b2c3d4",
"dispatch_state": "started"
}
},
"errors": [],
"execution_status_url": "/api/v1/studies/42/executions/exec_73fd5b985c6a4b629e2e5411bbd4bd12",
"created_at": "2026-06-03T12:05:00Z",
"updated_at": "2026-06-03T12:05:02Z",
"started_at": "2026-06-03T12:05:02Z",
"finished_at": null
}
],
"pagination": {
"limit": 50,
"offset": 0,
"count": 1,
"total": 1,
"next_offset": null,
"previous_offset": null
}
}
Cada item da lista usa o mesmo formato do endpoint de detalhe
GET /api/v1/studies/{study_id}/executions/{execution_id}.
GET /api/v1/studies/{study_id}/executions/{execution_id}
Consulta uma execução específica.
Escopo exigido: executions:read.
Estados possíveis:
| Status | Significado |
|---|---|
queued | Execução solicitada e aguardando servidor ou worker. |
running | Pelo menos uma rodada está ativa. |
completed | Todas as rodadas terminaram com sucesso. |
nonconverged | Rodadas terminaram sem convergência ou com mistura de sucesso e não convergência. |
failed | Falha operacional. |
stopped | Execução parada. |
partial | Mistura de estados terminais. |
Rodadas changed são tratadas como terminal não enfileirado: todas changed
consolidam como stopped; mistura de completed com changed consolida como
partial.
GET /api/v1/studies/{study_id}/executions/{execution_id}/rodadas/{rodada_id_or_name}/outputs/deckoutput.json
GET /api/v1/studies/{study_id}/executions/{execution_id}/rodadas/{rodada_id_or_name}/outputs/deckoutput.json
Lê o deckoutput.json gerado para uma rodada rastreada por uma execução da API
v1. A leitura exige run_id e snapshot de output gravados na execução, para não
usar o path atual de uma rodada reexecutada ou editada; o ref do snapshot usa
o commit de upload quando disponível, ou main enquanto o commit final ainda
não foi registrado. Este endpoint retorna apenas o output estruturado
deckoutput.json; outros arquivos da rodada continuam disponíveis pelo
Gitea/Git.
Escopo exigido: executions:read.
rodada_id_or_name aceita o id numérico da rodada ou o nome exato.
curl -sS \
"$MYRIA_URL/api/v1/studies/$STUDY_ID/executions/$EXECUTION_ID/rodadas/DC202605-sem3/outputs/deckoutput.json" \
-H "Authorization: Bearer $MYRIA_API_KEY"
Resposta:
{
"success": true,
"study_id": 201,
"execution_id": "exec_aa996401c6984767a4cc4e46cc4afc19",
"rodada": {
"id": 1529,
"name": "DC202605-sem3",
"type": "decomp",
"status": "completed",
"run_id": "1f1836ab3ae748b88b00f095c5ab8c3b"
},
"output": {
"name": "deckoutput.json",
"content_type": "application/json",
"size_bytes": 12345,
"gitea": {
"org": "org-teko",
"repo": "estudos",
"path": "estudo_201/rodada_1529/deckoutput.json",
"ref": "main"
},
"content": {
"PLD": {},
"earm": {},
"carga": {
"SE": {
"estagios": {
"1": {
"pesado": {"carga": 52409.0, "horas": 30.0},
"medio": {"carga": 52370.0, "horas": 74.0},
"leve": {"carga": 43304.0, "horas": 64.0},
"media_sub": {"carga": 48923.0, "horas": 168.0}
}
}
}
},
"ena": {
"SE": {
"Sem_01": 27789.09,
"Sem_02": 25662.11,
"Sem_03": 27606.72,
"Sem_04": 27305.28,
"Sem_05": 24930.24,
"Sem_06": 22997.70,
"Mes_01": 25950.00,
"Mes_01_perc_MLT": 86.50
},
"S": {
"Sem_01": 7630.03,
"Sem_02": 5217.11,
"Sem_03": 6359.73,
"Sem_04": 7015.60,
"Sem_05": 9735.19,
"Sem_06": 10424.62,
"Mes_01": 7530.00,
"Mes_01_perc_MLT": 75.30
},
"NE": {
"Sem_01": 4239.86,
"Sem_02": 3936.92,
"Sem_03": 3727.83,
"Sem_04": 3548.65,
"Sem_05": 3524.96,
"Sem_06": 3558.38,
"Mes_01": 3790.00,
"Mes_01_perc_MLT": 75.80
},
"N": {
"Sem_01": 10009.70,
"Sem_02": 8189.19,
"Sem_03": 6876.42,
"Sem_04": 5810.95,
"Sem_05": 4881.93,
"Sem_06": 4139.34,
"Mes_01": 7000.00,
"Mes_01_perc_MLT": 87.50
}
}
}
}
}
Regras principais:
cargavem dos registrosDPdo DADGER, agrupada pelos submercadosSE,S,NEeN, por estagio e patamar (pesado,medio,leve);- cada patamar contem
cargaehoras; valores numericos ausentes sao retornados comonull, sem incluir comentarios do DADGER; media_sub.horassoma as horas depesado,medioeleve, enquantomedia_sub.cargacalcula a media das cargas ponderada por essas horas e a arredonda para o inteiro mais proximo, mantendo representacao decimal (.0);- se algum patamar nao tiver carga ou horas,
media_sub.cargaretornanullemedia_sub.horascontinua somando as horas disponiveis; - se essa leitura falhar,
cargaretorna{}ediagnosticos.cargainforma o motivo sem remover os demais dados; - em rodadas DECOMP,
enae calculada a partir deprevs.rvX,REGRAS.DAT,hidr.date da produtividade do estagio 1 derelato.rvX; - a ENA e agregada por submercado e sempre usa
Sem_01aSem_06, independentemente da revisao; Mes_01representa a media mensal da ENA do mes da rodada, ponderando os dias daquele mes presentes em cada uma das seis semanas;Mes_01_perc_MLTrepresentaMes_01dividido pela media mensal de longo termo deorg-myria/arquivos/ena/mlt_ena.json, multiplicado por 100 e arredondado para 2 casas decimais;- se a tabela de MLT estiver ausente, incompleta ou invalida, a chave
Mes_01_perc_MLTfica ausente para os submercados afetados ediagnosticos.ena_mltinforma o motivo; - se o calculo de ENA falhar, o restante do
deckoutput.jsoncontinua disponivel,enaretorna{}ediagnosticos.enainforma o motivo; - a execução precisa estar em status terminal (
completed,nonconverged,failed,stoppedoupartial); - a rodada precisa pertencer ao snapshot da execução;
- se o snapshot da execução tem
run_id, ele precisa corresponder aorun_idatual da rodada; a API não retorna output de uma reexecução posterior; - o path do arquivo é derivado da rodada; o cliente não envia path livre;
- o arquivo é retornado inline até o limite operacional
MYRIA_API_V1_OUTPUT_MAX_BYTES; - se o arquivo não existe ou não corresponde ao
run_idda execução, a API retorna404 output_not_found.
Exemplos em Python
Os exemplos usam requests e podem ser colados em um arquivo local. Configure
MYRIA_URL com a URL do Portal e MYRIA_API_KEY com a chave criada no perfil.
pip install requests
export MYRIA_URL="https://portal.exemplo.com"
export MYRIA_API_KEY="myria_live_<identificador>.<segredo>"
Cliente pronto
import json
import os
import time
import uuid
from pathlib import Path
from typing import Any
import requests
TERMINAL_EXECUTION_STATUSES = {
"completed",
"nonconverged",
"failed",
"stopped",
"partial",
}
class MyriaApiError(RuntimeError):
def __init__(
self,
status_code: int,
code: str,
message: str,
*,
details: dict[str, Any] | None = None,
retry_after: str | None = None,
):
super().__init__(f"{status_code} {code}: {message}")
self.status_code = status_code
self.code = code
self.message = message
self.details = details or {}
self.retry_after = retry_after
class MyriaClient:
def __init__(self, base_url: str, api_key: str):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update({"Authorization": f"Bearer {api_key}"})
@classmethod
def from_env(cls) -> "MyriaClient":
return cls(os.environ["MYRIA_URL"], os.environ["MYRIA_API_KEY"])
def _request(self, method: str, path: str, **kwargs) -> dict[str, Any]:
response = self.session.request(
method,
f"{self.base_url}{path}",
timeout=kwargs.pop("timeout", 30),
**kwargs,
)
if response.ok:
return response.json()
try:
payload = response.json()
except ValueError:
response.raise_for_status()
error = payload.get("error", {})
raise MyriaApiError(
response.status_code,
error.get("code", "unknown_error"),
error.get("message", response.text),
details=error.get("details") or {},
retry_after=response.headers.get("Retry-After"),
)
def create_fast_track(
self,
*,
title: str | None = None,
org_slug: str | None = None,
idempotency_key: str,
) -> dict[str, Any]:
payload: dict[str, Any] = {"template": "fast_track"}
if title:
payload["title"] = title
if org_slug:
payload["org_slug"] = org_slug
return self._request(
"POST",
"/api/v1/studies",
headers={"Idempotency-Key": idempotency_key},
json=payload,
)
def import_zip(
self,
zip_path: str,
*,
title: str | None = None,
selected_rodadas: list[str] | None = None,
rodada_options: dict[str, Any] | None = None,
org_slug: str | None = None,
idempotency_key: str,
) -> dict[str, Any]:
data: list[tuple[str, str]] = []
if title:
data.append(("title", title))
if org_slug:
data.append(("org_slug", org_slug))
for rodada in selected_rodadas or []:
data.append(("selected_rodadas[]", rodada))
if rodada_options:
data.append(("rodada_options", json.dumps(rodada_options)))
path = Path(zip_path)
with path.open("rb") as file_obj:
return self._request(
"POST",
"/api/v1/studies/imports",
headers={"Idempotency-Key": idempotency_key},
files={"file": (path.name, file_obj, "application/zip")},
data=data,
timeout=60,
)
def get_import(self, import_id: str) -> dict[str, Any]:
return self._request("GET", f"/api/v1/studies/imports/{import_id}")
def get_study(self, study_id: int) -> dict[str, Any]:
return self._request("GET", f"/api/v1/studies/{study_id}")
def add_incremental(
self,
study_id: int,
*,
mode: str = "rv",
count: int = 1,
idempotency_key: str,
) -> dict[str, Any]:
return self._request(
"POST",
f"/api/v1/studies/{study_id}/incremental-operations",
headers={"Idempotency-Key": idempotency_key},
json={"mode": mode, "count": count},
)
def get_incremental(self, study_id: int, operation_id: str) -> dict[str, Any]:
return self._request(
"GET",
f"/api/v1/studies/{study_id}/incremental-operations/{operation_id}",
)
def patch_rodada_options(
self,
study_id: int,
rodada_id_or_name: str,
options: dict[str, Any],
*,
idempotency_key: str,
) -> dict[str, Any]:
return self._request(
"PATCH",
f"/api/v1/studies/{study_id}/rodadas/{rodada_id_or_name}/options",
headers={"Idempotency-Key": idempotency_key},
json=options,
)
def start_execution(
self,
study_id: int,
*,
execution_options: dict[str, Any] | None = None,
rodada_options: dict[str, Any] | None = None,
idempotency_key: str,
) -> dict[str, Any]:
payload: dict[str, Any] = {}
if execution_options:
payload["execution_options"] = execution_options
if rodada_options:
payload["rodada_options"] = rodada_options
return self._request(
"POST",
f"/api/v1/studies/{study_id}/executions",
headers={"Idempotency-Key": idempotency_key},
json=payload,
)
def list_executions(
self,
study_id: int,
*,
limit: int = 50,
offset: int = 0,
) -> dict[str, Any]:
return self._request(
"GET",
f"/api/v1/studies/{study_id}/executions",
params={"limit": limit, "offset": offset},
)
def get_execution(self, study_id: int, execution_id: str) -> dict[str, Any]:
return self._request(
"GET",
f"/api/v1/studies/{study_id}/executions/{execution_id}",
)
def get_deckoutput(
self,
study_id: int,
execution_id: str,
rodada_id_or_name: str,
) -> dict[str, Any]:
return self._request(
"GET",
"/api/v1/studies/"
f"{study_id}/executions/{execution_id}/rodadas/"
f"{rodada_id_or_name}/outputs/deckoutput.json",
)
def wait_import_finished(
self,
import_id: str,
*,
interval_seconds: int = 10,
timeout_seconds: int = 1800,
) -> dict[str, Any]:
deadline = time.monotonic() + timeout_seconds
while True:
job = self.get_import(import_id)
if job["status"] in {"ready", "blocked", "failed"}:
return job
if time.monotonic() > deadline:
raise TimeoutError(f"Importacao {import_id} nao terminou a tempo")
time.sleep(interval_seconds)
def wait_study_ready(
self,
study_id: int,
*,
interval_seconds: int = 10,
timeout_seconds: int = 1800,
) -> dict[str, Any]:
deadline = time.monotonic() + timeout_seconds
while True:
study = self.get_study(study_id)["study"]
if study["ready_for_execution"]:
return study
if study["status"] in {"blocked", "failed"}:
raise RuntimeError(study)
if time.monotonic() > deadline:
raise TimeoutError(f"Estudo {study_id} nao ficou pronto a tempo")
time.sleep(interval_seconds)
def wait_incremental_finished(
self,
study_id: int,
operation_id: str,
*,
interval_seconds: int = 10,
timeout_seconds: int = 1800,
) -> dict[str, Any]:
deadline = time.monotonic() + timeout_seconds
while True:
operation = self.get_incremental(study_id, operation_id)
if operation["status"] in {"completed", "failed", "cancelled"}:
return operation
if time.monotonic() > deadline:
raise TimeoutError(f"Operacao incremental {operation_id} nao terminou a tempo")
time.sleep(interval_seconds)
def wait_execution_terminal(
self,
study_id: int,
execution_id: str,
*,
interval_seconds: int = 15,
timeout_seconds: int = 7200,
) -> dict[str, Any]:
deadline = time.monotonic() + timeout_seconds
while True:
execution = self.get_execution(study_id, execution_id)
if execution["status"] in TERMINAL_EXECUTION_STATUSES:
return execution
if time.monotonic() > deadline:
raise TimeoutError(f"Execucao {execution_id} nao terminou a tempo")
time.sleep(interval_seconds)
def idem(prefix: str) -> str:
return f"{prefix}-{uuid.uuid4().hex}"
Criar Fast Track e aguardar readiness
client = MyriaClient.from_env()
created = client.create_fast_track(
title="Fast Track Junho 2026",
idempotency_key=idem("create-fast-track"),
)
study_id = created["study_id"]
study = client.wait_study_ready(study_id)
print("study_id:", study_id)
print("status:", study["status"])
Importar ZIP com opções de rodada
client = MyriaClient.from_env()
job = client.import_zip(
"decomp_202606_rv0.zip",
title="DECOMP PMO 202606 RV0",
selected_rodadas=["decomp_rv0"],
rodada_options={
"decomp_rv0": {
"prevs_file": "prevs.rv0",
"corte": {
"org_slug": "minha-org",
"gitea_repo": "estudos",
"gitea_path": "estudo_120/rodada_991",
},
}
},
idempotency_key=idem("import-decomp-rv0"),
)
job = client.wait_import_finished(job["import_id"])
if job["status"] == "failed":
raise RuntimeError(job["errors"])
study = client.wait_study_ready(job["study_id"])
print("study_id:", study["id"])
Adicionar RV+ e ajustar EARM de entrada
client = MyriaClient.from_env()
study_id = 42
operation = client.add_incremental(
study_id,
mode="rv",
count=1,
idempotency_key=idem("rv-plus"),
)
operation = client.wait_incremental_finished(study_id, operation["operation_id"])
if operation["status"] != "completed":
raise RuntimeError(operation["error"])
new_rodada = operation["rodadas"][0]["name"]
client.patch_rodada_options(
study_id,
new_rodada,
{
"decomp_base": {
"org_slug": "minha-org",
"gitea_repo": "estudos",
"gitea_path": "estudo_120/rodada_991",
},
"decomp_base_earm_column": "inic",
},
idempotency_key=idem("patch-earm-base"),
)
Executar o estudo e ler deckoutput.json
client = MyriaClient.from_env()
study_id = 42
execution = client.start_execution(
study_id,
execution_options={
"decomp_cpu_cores": 8,
"decomp_max_inviab_attempts": 7,
},
rodada_options={
"decomp_rv0": {
"prevs_file": "prevs.rv0",
}
},
idempotency_key=idem("exec-study"),
)
execution = client.wait_execution_terminal(study_id, execution["execution_id"])
print("execution status:", execution["status"])
if execution["errors"]:
print("execution errors:", execution["errors"])
for rodada in execution["rodadas"]:
print(rodada["name"], rodada["status"], rodada.get("run_id"))
completed_rodadas = [
rodada for rodada in execution["rodadas"]
if rodada["status"] in {"completed", "nonconverged"}
]
if completed_rodadas:
output = client.get_deckoutput(
study_id,
execution["execution_id"],
completed_rodadas[0]["name"],
)
print(output["output"]["content"].keys())
Listar execuções recentes
client = MyriaClient.from_env()
page = client.list_executions(42, limit=20)
for execution in page["executions"]:
print(execution["execution_id"], execution["status"], execution["created_at"])
if page["pagination"]["next_offset"] is not None:
next_page = client.list_executions(
42,
limit=20,
offset=page["pagination"]["next_offset"],
)
Erros
Todos os erros seguem este formato:
{
"success": false,
"error": {
"code": "invalid_zip",
"message": "Arquivo deve ser um ZIP valido.",
"details": {
"reason": "signature"
}
}
}
Disponibilidade
Quando MYRIA_API_V1_ENABLED=false, endpoints v1 retornam 404 api_v1_disabled.
Quando MYRIA_API_V1_EXECUTIONS_ENABLED=false, endpoints de execução retornam
404 api_v1_executions_disabled.
Códigos comuns:
| HTTP | Código | Quando ocorre |
|---|---|---|
400 | invalid_json | JSON malformado. |
400 | invalid_payload | Payload fora do formato esperado. |
400 | invalid_template | Template de criação de estudo desconhecido. |
400 | invalid_mode | Modo incremental diferente de rv ou m. |
400 | invalid_count | Quantidade incremental fora de 1..24. |
400 | empty_study | Estudo não tem rodadas principais para servir de base incremental. |
400 | invalid_zip | Arquivo não é um ZIP válido, inclui entradas corrompidas ou ZIP aninhado inválido. |
400 | missing_file | Upload sem campo file. |
400 | invalid_execution_options | Opção de execução desconhecida, valor inválido ou decomp_cpu_cores sem capacidade conhecida ou acima da maior capacidade conhecida. |
400 | unknown_rodada | A rodada informada nao existe, nao foi importada ou nao pode ser resolvida pelo identificador enviado. |
400 | invalid_output | Output solicitado existe, mas não é JSON válido no formato esperado. |
409 | ambiguous_rodada | O nome informado para a rodada bate em mais de uma rodada possivel. |
409 | study_conflict | O estudo esta compartilhado ou em mutacao ativa. |
400/403/404 | unsupported_rodada_option | A opcao pedida nao se aplica ao tipo de rodada, ownership ou binario informado. |
400 | invalid_idempotency_key | Idempotency-Key inválida. |
401 | missing_api_key | Header Authorization ausente. |
401 | invalid_api_key | Chave malformada ou segredo inválido. |
401 | expired_api_key | Chave expirada. |
401 | revoked_api_key | Chave revogada. |
403 | insufficient_scope | Chave sem escopo exigido. |
403 | forbidden | Chave válida sem permissão para o recurso da organização. |
403 | organization_mismatch | org_slug diferente da organização da chave. |
405 | method_not_allowed | Método HTTP fora do contrato do endpoint. |
404 | not_found | Recurso inexistente ou de outra organização. |
404 | api_v1_disabled | API v1 desabilitada no ambiente. |
404 | api_v1_executions_disabled | Execuções via API v1 desabilitadas no ambiente. |
404 | output_not_found | Output solicitado não existe no path materializado da rodada ou o run_id atual da rodada não corresponde ao snapshot da execução. |
409 | idempotency_conflict | Mesma chave idempotente com payload ou arquivo diferente. |
409 | unsupported_execution_mode | Execução parcial, reexecução, resume, reset ou prioridade explícita não suportada. |
409 | study_not_ready | Estudo ainda não está pronto para execução. |
409 | active_execution | Já existe execução ativa conflitante. |
409 | no_active_server | Organização não tem servidor ativo nem overflow OCI elegível. |
409 | study_mutation_conflict | Estudo tem importação, validação ou mutação incremental em andamento. |
409 | active_incremental_operation | Já existe operação incremental em andamento para o estudo. |
409 | shared_deck | Recurso é deck compartilhado, não estudo operacional. |
409 | shared_deck_not_executable | Recurso é deck compartilhado, não estudo operacional. |
409 | pre_dispatch_failed | Configuração impede o despacho, por exemplo binário ausente. |
409 | output_not_ready | Output solicitado antes de a execução chegar a status terminal. |
413 | file_too_large | ZIP excede limite permitido. |
413 | output_too_large | Output JSON excede o limite operacional de retorno inline. |
415 | unsupported_media_type | Corpo não vazio enviado com Content-Type fora de JSON/form/multipart. |
429 | rate_limited | Limite de chamadas excedido. |
500 | study_creation_failed | Falha inesperada ao criar estudo Fast Track. |
500 | output_read_failed | Falha operacional ao ler output no Gitea. |
500 | incremental_operation_failed | Falha inesperada antes de a operação incremental virar recurso consultável. Falhas depois da persistência retornam o próprio recurso com status: "failed". |
503 | dispatch_failed | Falha ao enfileirar processamento. |
503 | fast_track_unavailable | Falha ao descobrir a base Fast Track sem criar estudo parcial. error.details.reason usa código público estável e não expõe paths ou mensagens internas do backend. |
Boas práticas
- Use
Idempotency-Keyem todoPOSTe no PATCH de opções de rodada. - Separe chaves por uso: uma para importação e outra para execução, se possível.
- Guarde a chave em variável de ambiente, nunca no código.
- Trate
429respeitando o cabeçalhoRetry-After. - Consulte o status por polling com intervalo de alguns segundos; evite loops sem espera.
- Não reenvie o ZIP com outra idempotency key antes de verificar o status da importação original.