[{"data":1,"prerenderedAt":1674},["ShallowReactive",2],{"doc-\u002Fdocs\u002Fapi\u002Freferencia-api":3,"docs-all":1606},{"id":4,"title":5,"body":6,"category":1588,"description":1589,"draft":1590,"extension":1591,"icon":1592,"lastReviewed":1593,"meta":1594,"navigation":1595,"order":76,"path":1596,"prerequisites":1597,"readingTime":1598,"seo":1599,"stem":1600,"tags":1601,"__hash__":1605},"docs_pt\u002Fdocs\u002Fapi\u002Freferencia-api.md","Referência da API REST",{"type":7,"value":8,"toc":1565},"minimark",[9,18,21,26,36,43,58,62,65,89,95,100,146,149,205,208,211,243,247,250,281,285,289,292,364,367,404,407,451,455,458,508,541,545,548,598,601,638,648,652,702,733,737,740,800,803,848,852,890,920,924,927,965,968,995,1001,1007,1011,1014,1052,1072,1076,1079,1117,1147,1151,1154,1180,1196,1199,1202,1206,1213,1245,1251,1254,1258,1351,1354,1401,1408,1412,1415,1474,1477,1521,1532,1536,1561],[10,11,12,13,17],"p",{},"A API do HeroCtl é o canal único entre você, o CLI e a interface web. Tudo que o painel mostra passa por aqui. Tudo que o ",[14,15,16],"code",{},"heroctl"," faz na linha de comando também.",[10,19,20],{},"A API é REST. JSON na entrada, JSON na saída. Sem GraphQL. Sem gRPC público.",[22,23,25],"h2",{"id":24},"endpoint-base","Endpoint base",[27,28,33],"pre",{"className":29,"code":31,"language":32},[30],"language-text","https:\u002F\u002Fmanage.\u003Cseu-dominio>\u002Fv1\u002F\n","text",[14,34,31],{"__ignoreMap":35},"",[10,37,38,39,42],{},"Em produção padrão: ",[14,40,41],{},"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002F",".",[10,44,45,46,49,50,53,54,57],{},"Todas as rotas vivem sob ",[14,47,48],{},"\u002Fv1\u002F",". Quando o ",[14,51,52],{},"v2"," chegar, o ",[14,55,56],{},"v1"," continua respondendo por pelo menos 12 meses.",[22,59,61],{"id":60},"autenticacao","Autenticação",[10,63,64],{},"A API usa JWT. Você obtém um token via login e envia em cada requisição:",[27,66,70],{"className":67,"code":68,"language":69,"meta":35,"style":35},"language-http shiki shiki-themes github-dark-default","Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n","http",[14,71,72],{"__ignoreMap":35},[73,74,77,81,85],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"sPWt5","Authorization",[73,82,84],{"class":83},"suJrU",":",[73,86,88],{"class":87},"s9uIt"," Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n",[10,90,91,92,42],{},"Sem header, sem resposta. O servidor responde ",[14,93,94],{},"401",[96,97,99],"h3",{"id":98},"login","Login",[27,101,105],{"className":102,"code":103,"language":104,"meta":35,"style":35},"language-bash shiki shiki-themes github-dark-default","curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fauth\u002Flogin \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"username\":\"admin\",\"password\":\"admin123\"}'\n","bash",[14,106,107,126,137],{"__ignoreMap":35},[73,108,109,113,117,120,123],{"class":75,"line":76},[73,110,112],{"class":111},"sQhOw","curl",[73,114,116],{"class":115},"sFSAA"," -X",[73,118,119],{"class":87}," POST",[73,121,122],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fauth\u002Flogin",[73,124,125],{"class":83}," \\\n",[73,127,129,132,135],{"class":75,"line":128},2,[73,130,131],{"class":115},"  -H",[73,133,134],{"class":87}," \"Content-Type: application\u002Fjson\"",[73,136,125],{"class":83},[73,138,140,143],{"class":75,"line":139},3,[73,141,142],{"class":115},"  -d",[73,144,145],{"class":87}," '{\"username\":\"admin\",\"password\":\"admin123\"}'\n",[10,147,148],{},"Resposta:",[27,150,154],{"className":151,"code":152,"language":153,"meta":35,"style":35},"language-json shiki shiki-themes github-dark-default","{\n  \"token\": \"eyJhbGciOi...\",\n  \"expires_at\": \"2026-04-27T15:00:00Z\",\n  \"refresh_token\": \"rt_8f3a...\"\n}\n","json",[14,155,156,162,176,188,199],{"__ignoreMap":35},[73,157,158],{"class":75,"line":76},[73,159,161],{"class":160},"sZEs4","{\n",[73,163,164,167,170,173],{"class":75,"line":128},[73,165,166],{"class":79},"  \"token\"",[73,168,169],{"class":160},": ",[73,171,172],{"class":87},"\"eyJhbGciOi...\"",[73,174,175],{"class":160},",\n",[73,177,178,181,183,186],{"class":75,"line":139},[73,179,180],{"class":79},"  \"expires_at\"",[73,182,169],{"class":160},[73,184,185],{"class":87},"\"2026-04-27T15:00:00Z\"",[73,187,175],{"class":160},[73,189,191,194,196],{"class":75,"line":190},4,[73,192,193],{"class":79},"  \"refresh_token\"",[73,195,169],{"class":160},[73,197,198],{"class":87},"\"rt_8f3a...\"\n",[73,200,202],{"class":75,"line":201},5,[73,203,204],{"class":160},"}\n",[10,206,207],{},"O token padrão dura 24 horas.",[10,209,210],{},"Você também pode autenticar com um ACL token (gerado pela interface ou pelo CLI):",[27,212,214],{"className":102,"code":213,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fauth\u002Flogin \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"acl_token\":\"ht_a1b2c3d4...\"}'\n",[14,215,216,228,236],{"__ignoreMap":35},[73,217,218,220,222,224,226],{"class":75,"line":76},[73,219,112],{"class":111},[73,221,116],{"class":115},[73,223,119],{"class":87},[73,225,122],{"class":87},[73,227,125],{"class":83},[73,229,230,232,234],{"class":75,"line":128},[73,231,131],{"class":115},[73,233,134],{"class":87},[73,235,125],{"class":83},[73,237,238,240],{"class":75,"line":139},[73,239,142],{"class":115},[73,241,242],{"class":87}," '{\"acl_token\":\"ht_a1b2c3d4...\"}'\n",[96,244,246],{"id":245},"refresh","Refresh",[10,248,249],{},"Antes do token expirar, troque por um novo:",[27,251,253],{"className":102,"code":252,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fauth\u002Frefresh \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[14,254,255,268],{"__ignoreMap":35},[73,256,257,259,261,263,266],{"class":75,"line":76},[73,258,112],{"class":111},[73,260,116],{"class":115},[73,262,119],{"class":87},[73,264,265],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fauth\u002Frefresh",[73,267,125],{"class":83},[73,269,270,272,275,278],{"class":75,"line":128},[73,271,131],{"class":115},[73,273,274],{"class":87}," \"Authorization: Bearer ",[73,276,277],{"class":160},"$TOKEN",[73,279,280],{"class":87},"\"\n",[22,282,284],{"id":283},"endpoints-principais","Endpoints principais",[96,286,288],{"id":287},"jobs","Jobs",[10,290,291],{},"Um job é a definição declarativa do que deve rodar. O cluster cuida do resto.",[293,294,295,311],"table",{},[296,297,298],"thead",{},[299,300,301,305,308],"tr",{},[302,303,304],"th",{},"Método",[302,306,307],{},"Rota",[302,309,310],{},"O que faz",[312,313,314,328,340,352],"tbody",{},[299,315,316,320,325],{},[317,318,319],"td",{},"GET",[317,321,322],{},[14,323,324],{},"\u002Fv1\u002Fjobs",[317,326,327],{},"Lista todos os jobs",[299,329,330,332,337],{},[317,331,319],{},[317,333,334],{},[14,335,336],{},"\u002Fv1\u002Fjobs\u002F:name",[317,338,339],{},"Detalhe de um job",[299,341,342,345,349],{},[317,343,344],{},"POST",[317,346,347],{},[14,348,324],{},[317,350,351],{},"Submete ou atualiza um job",[299,353,354,357,361],{},[317,355,356],{},"DELETE",[317,358,359],{},[14,360,336],{},[317,362,363],{},"Remove o job e suas instâncias",[10,365,366],{},"Listar jobs:",[27,368,370],{"className":102,"code":369,"language":104,"meta":35,"style":35},"curl -s https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs \\\n  -H \"Authorization: Bearer $TOKEN\" | jq '.[] | .name'\n",[14,371,372,384],{"__ignoreMap":35},[73,373,374,376,379,382],{"class":75,"line":76},[73,375,112],{"class":111},[73,377,378],{"class":115}," -s",[73,380,381],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs",[73,383,125],{"class":83},[73,385,386,388,390,392,395,398,401],{"class":75,"line":128},[73,387,131],{"class":115},[73,389,274],{"class":87},[73,391,277],{"class":160},[73,393,394],{"class":87},"\"",[73,396,397],{"class":83}," |",[73,399,400],{"class":111}," jq",[73,402,403],{"class":87}," '.[] | .name'\n",[10,405,406],{},"Submeter um job:",[27,408,410],{"className":102,"code":409,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d @meu-job.json\n",[14,411,412,424,436,444],{"__ignoreMap":35},[73,413,414,416,418,420,422],{"class":75,"line":76},[73,415,112],{"class":111},[73,417,116],{"class":115},[73,419,119],{"class":87},[73,421,381],{"class":87},[73,423,125],{"class":83},[73,425,426,428,430,432,434],{"class":75,"line":128},[73,427,131],{"class":115},[73,429,274],{"class":87},[73,431,277],{"class":160},[73,433,394],{"class":87},[73,435,125],{"class":83},[73,437,438,440,442],{"class":75,"line":139},[73,439,131],{"class":115},[73,441,134],{"class":87},[73,443,125],{"class":83},[73,445,446,448],{"class":75,"line":190},[73,447,142],{"class":115},[73,449,450],{"class":87}," @meu-job.json\n",[96,452,454],{"id":453},"allocations","Allocations",[10,456,457],{},"Cada réplica de um job vira uma allocation. É a unidade de execução real.",[293,459,460,470],{},[296,461,462],{},[299,463,464,466,468],{},[302,465,304],{},[302,467,307],{},[302,469,310],{},[312,471,472,484,496],{},[299,473,474,476,481],{},[317,475,319],{},[317,477,478],{},[14,479,480],{},"\u002Fv1\u002Fallocations",[317,482,483],{},"Lista allocations",[299,485,486,488,493],{},[317,487,319],{},[317,489,490],{},[14,491,492],{},"\u002Fv1\u002Fallocations\u002F:id",[317,494,495],{},"Detalhe",[299,497,498,500,505],{},[317,499,344],{},[317,501,502],{},[14,503,504],{},"\u002Fv1\u002Fallocations\u002F:id\u002Fstop",[317,506,507],{},"Para uma allocation",[27,509,511],{"className":102,"code":510,"language":104,"meta":35,"style":35},"curl -s \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fallocations?job=heroctl-site\" \\\n  -H \"Authorization: Bearer $TOKEN\" | jq '.[] | {id, node, status}'\n",[14,512,513,524],{"__ignoreMap":35},[73,514,515,517,519,522],{"class":75,"line":76},[73,516,112],{"class":111},[73,518,378],{"class":115},[73,520,521],{"class":87}," \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fallocations?job=heroctl-site\"",[73,523,125],{"class":83},[73,525,526,528,530,532,534,536,538],{"class":75,"line":128},[73,527,131],{"class":115},[73,529,274],{"class":87},[73,531,277],{"class":160},[73,533,394],{"class":87},[73,535,397],{"class":83},[73,537,400],{"class":111},[73,539,540],{"class":87}," '.[] | {id, node, status}'\n",[96,542,544],{"id":543},"nodes","Nodes",[10,546,547],{},"Os servidores que rodam workloads.",[293,549,550,560],{},[296,551,552],{},[299,553,554,556,558],{},[302,555,304],{},[302,557,307],{},[302,559,310],{},[312,561,562,574,586],{},[299,563,564,566,571],{},[317,565,319],{},[317,567,568],{},[14,569,570],{},"\u002Fv1\u002Fnodes",[317,572,573],{},"Lista nós",[299,575,576,578,583],{},[317,577,319],{},[317,579,580],{},[14,581,582],{},"\u002Fv1\u002Fnodes\u002F:id",[317,584,585],{},"Detalhe (cpu, ram, disk, status)",[299,587,588,590,595],{},[317,589,344],{},[317,591,592],{},[14,593,594],{},"\u002Fv1\u002Fnodes\u002F:id\u002Fdrain",[317,596,597],{},"Drena um nó (move workloads pra fora)",[10,599,600],{},"Drenar um nó antes de manutenção:",[27,602,604],{"className":102,"code":603,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fnodes\u002Fserver-2\u002Fdrain \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\"deadline_seconds\": 300}'\n",[14,605,606,619,631],{"__ignoreMap":35},[73,607,608,610,612,614,617],{"class":75,"line":76},[73,609,112],{"class":111},[73,611,116],{"class":115},[73,613,119],{"class":87},[73,615,616],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fnodes\u002Fserver-2\u002Fdrain",[73,618,125],{"class":83},[73,620,621,623,625,627,629],{"class":75,"line":128},[73,622,131],{"class":115},[73,624,274],{"class":87},[73,626,277],{"class":160},[73,628,394],{"class":87},[73,630,125],{"class":83},[73,632,633,635],{"class":75,"line":139},[73,634,142],{"class":115},[73,636,637],{"class":87}," '{\"deadline_seconds\": 300}'\n",[639,640,641],"blockquote",{},[10,642,643,647],{},[644,645,646],"strong",{},"Atenção:"," drain remove todas as allocations do nó. Garanta capacidade nos demais antes.",[96,649,651],{"id":650},"cluster","Cluster",[293,653,654,664],{},[296,655,656],{},[299,657,658,660,662],{},[302,659,304],{},[302,661,307],{},[302,663,310],{},[312,665,666,678,690],{},[299,667,668,670,675],{},[317,669,319],{},[317,671,672],{},[14,673,674],{},"\u002Fv1\u002Fcluster\u002Fstatus",[317,676,677],{},"Saúde geral, nó coordenador, número de membros",[299,679,680,682,687],{},[317,681,319],{},[317,683,684],{},[14,685,686],{},"\u002Fv1\u002Fcluster\u002Fpeers",[317,688,689],{},"Lista de pares do plano de controle",[299,691,692,694,699],{},[317,693,344],{},[317,695,696],{},[14,697,698],{},"\u002Fv1\u002Fraft\u002Ftransfer-leadership",[317,700,701],{},"Transfere coordenação (admin)",[27,703,705],{"className":102,"code":704,"language":104,"meta":35,"style":35},"curl -s https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fcluster\u002Fstatus \\\n  -H \"Authorization: Bearer $TOKEN\" | jq\n",[14,706,707,718],{"__ignoreMap":35},[73,708,709,711,713,716],{"class":75,"line":76},[73,710,112],{"class":111},[73,712,378],{"class":115},[73,714,715],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fcluster\u002Fstatus",[73,717,125],{"class":83},[73,719,720,722,724,726,728,730],{"class":75,"line":128},[73,721,131],{"class":115},[73,723,274],{"class":87},[73,725,277],{"class":160},[73,727,394],{"class":87},[73,729,397],{"class":83},[73,731,732],{"class":111}," jq\n",[96,734,736],{"id":735},"secrets","Secrets",[10,738,739],{},"Valores sensíveis. Armazenados criptografados em repouso.",[293,741,742,752],{},[296,743,744],{},[299,745,746,748,750],{},[302,747,304],{},[302,749,307],{},[302,751,310],{},[312,753,754,766,777,789],{},[299,755,756,758,763],{},[317,757,319],{},[317,759,760],{},[14,761,762],{},"\u002Fv1\u002Fsecrets",[317,764,765],{},"Lista nomes (nunca valores)",[299,767,768,770,774],{},[317,769,344],{},[317,771,772],{},[14,773,762],{},[317,775,776],{},"Cria ou atualiza",[299,778,779,781,786],{},[317,780,319],{},[317,782,783],{},[14,784,785],{},"\u002Fv1\u002Fsecrets\u002F:name",[317,787,788],{},"Lê um valor (precisa de ACL específica)",[299,790,791,793,797],{},[317,792,356],{},[317,794,795],{},[14,796,785],{},[317,798,799],{},"Apaga",[10,801,802],{},"Criar um secret:",[27,804,806],{"className":102,"code":805,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fsecrets \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"name\":\"db_password\",\"value\":\"s3nh@-forte\"}'\n",[14,807,808,821,833,841],{"__ignoreMap":35},[73,809,810,812,814,816,819],{"class":75,"line":76},[73,811,112],{"class":111},[73,813,116],{"class":115},[73,815,119],{"class":87},[73,817,818],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fsecrets",[73,820,125],{"class":83},[73,822,823,825,827,829,831],{"class":75,"line":128},[73,824,131],{"class":115},[73,826,274],{"class":87},[73,828,277],{"class":160},[73,830,394],{"class":87},[73,832,125],{"class":83},[73,834,835,837,839],{"class":75,"line":139},[73,836,131],{"class":115},[73,838,134],{"class":87},[73,840,125],{"class":83},[73,842,843,845],{"class":75,"line":190},[73,844,142],{"class":115},[73,846,847],{"class":87}," '{\"name\":\"db_password\",\"value\":\"s3nh@-forte\"}'\n",[96,849,851],{"id":850},"metricas","Métricas",[293,853,854,864],{},[296,855,856],{},[299,857,858,860,862],{},[302,859,304],{},[302,861,307],{},[302,863,310],{},[312,865,866,878],{},[299,867,868,870,875],{},[317,869,319],{},[317,871,872],{},[14,873,874],{},"\u002Fv1\u002Fmetrics",[317,876,877],{},"Snapshot atual (CPU, mem, allocs, ingress)",[299,879,880,882,887],{},[317,881,319],{},[317,883,884],{},[14,885,886],{},"\u002Fv1\u002Fmetrics\u002Fquery",[317,888,889],{},"Query histórica com janela de tempo",[27,891,893],{"className":102,"code":892,"language":104,"meta":35,"style":35},"curl -s \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fmetrics\u002Fquery?metric=cpu&job=heroctl-site&from=-1h\" \\\n  -H \"Authorization: Bearer $TOKEN\" | jq\n",[14,894,895,906],{"__ignoreMap":35},[73,896,897,899,901,904],{"class":75,"line":76},[73,898,112],{"class":111},[73,900,378],{"class":115},[73,902,903],{"class":87}," \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fmetrics\u002Fquery?metric=cpu&job=heroctl-site&from=-1h\"",[73,905,125],{"class":83},[73,907,908,910,912,914,916,918],{"class":75,"line":128},[73,909,131],{"class":115},[73,911,274],{"class":87},[73,913,277],{"class":160},[73,915,394],{"class":87},[73,917,397],{"class":83},[73,919,732],{"class":111},[96,921,923],{"id":922},"logs","Logs",[10,925,926],{},"Logs vivem por allocation.",[293,928,929,939],{},[296,930,931],{},[299,932,933,935,937],{},[302,934,304],{},[302,936,307],{},[302,938,310],{},[312,940,941,953],{},[299,942,943,945,950],{},[317,944,319],{},[317,946,947],{},[14,948,949],{},"\u002Fv1\u002Flogs\u002F:alloc",[317,951,952],{},"Últimas N linhas",[299,954,955,957,962],{},[317,956,319],{},[317,958,959],{},[14,960,961],{},"\u002Fv1\u002Flogs?stream=true",[317,963,964],{},"Streaming via SSE",[10,966,967],{},"Streaming ao vivo:",[27,969,971],{"className":102,"code":970,"language":104,"meta":35,"style":35},"curl -N \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Flogs?job=heroctl-site&stream=true\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[14,972,973,985],{"__ignoreMap":35},[73,974,975,977,980,983],{"class":75,"line":76},[73,976,112],{"class":111},[73,978,979],{"class":115}," -N",[73,981,982],{"class":87}," \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Flogs?job=heroctl-site&stream=true\"",[73,984,125],{"class":83},[73,986,987,989,991,993],{"class":75,"line":128},[73,988,131],{"class":115},[73,990,274],{"class":87},[73,992,277],{"class":160},[73,994,280],{"class":87},[10,996,997,998,84],{},"A saída vem em formato ",[14,999,1000],{},"text\u002Fevent-stream",[27,1002,1005],{"className":1003,"code":1004,"language":32},[30],"event: log\ndata: {\"alloc\":\"a1b2\",\"line\":\"server started\"}\n",[14,1006,1004],{"__ignoreMap":35},[96,1008,1010],{"id":1009},"health","Health",[10,1012,1013],{},"Endpoints sem autenticação. Para uso de monitoramento externo.",[293,1015,1016,1026],{},[296,1017,1018],{},[299,1019,1020,1022,1024],{},[302,1021,304],{},[302,1023,307],{},[302,1025,310],{},[312,1027,1028,1040],{},[299,1029,1030,1032,1037],{},[317,1031,319],{},[317,1033,1034],{},[14,1035,1036],{},"\u002Fv1\u002Fhealth\u002Fready",[317,1038,1039],{},"Pronto para receber tráfego",[299,1041,1042,1044,1049],{},[317,1043,319],{},[317,1045,1046],{},[14,1047,1048],{},"\u002Fv1\u002Fhealth\u002Flive",[317,1050,1051],{},"Processo vivo",[27,1053,1055],{"className":102,"code":1054,"language":104,"meta":35,"style":35},"curl -s https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fhealth\u002Fready\n# {\"status\":\"ok\"}\n",[14,1056,1057,1066],{"__ignoreMap":35},[73,1058,1059,1061,1063],{"class":75,"line":76},[73,1060,112],{"class":111},[73,1062,378],{"class":115},[73,1064,1065],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fhealth\u002Fready\n",[73,1067,1068],{"class":75,"line":128},[73,1069,1071],{"class":1070},"sH3jZ","# {\"status\":\"ok\"}\n",[96,1073,1075],{"id":1074},"ingress","Ingress",[10,1077,1078],{},"Domínios servidos pelo cluster.",[293,1080,1081,1091],{},[296,1082,1083],{},[299,1084,1085,1087,1089],{},[302,1086,304],{},[302,1088,307],{},[302,1090,310],{},[312,1092,1093,1105],{},[299,1094,1095,1097,1102],{},[317,1096,319],{},[317,1098,1099],{},[14,1100,1101],{},"\u002Fv1\u002Fingress",[317,1103,1104],{},"Todos os domínios ativos",[299,1106,1107,1109,1114],{},[317,1108,319],{},[317,1110,1111],{},[14,1112,1113],{},"\u002Fv1\u002Fingress\u002F:host",[317,1115,1116],{},"Detalhe de um domínio (cert, rotas, job alvo)",[27,1118,1120],{"className":102,"code":1119,"language":104,"meta":35,"style":35},"curl -s https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fingress\u002Fheroctl.com \\\n  -H \"Authorization: Bearer $TOKEN\" | jq\n",[14,1121,1122,1133],{"__ignoreMap":35},[73,1123,1124,1126,1128,1131],{"class":75,"line":76},[73,1125,112],{"class":111},[73,1127,378],{"class":115},[73,1129,1130],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fingress\u002Fheroctl.com",[73,1132,125],{"class":83},[73,1134,1135,1137,1139,1141,1143,1145],{"class":75,"line":128},[73,1136,131],{"class":115},[73,1138,274],{"class":87},[73,1140,277],{"class":160},[73,1142,394],{"class":87},[73,1144,397],{"class":83},[73,1146,732],{"class":111},[22,1148,1150],{"id":1149},"long-polling","Long polling",[10,1152,1153],{},"Vários endpoints suportam espera bloqueante. Em vez de fazer poll a cada segundo, você passa um índice e o tempo máximo de espera. A resposta volta quando algo muda — ou quando o tempo acaba.",[27,1155,1157],{"className":102,"code":1156,"language":104,"meta":35,"style":35},"curl -s \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs?wait=5m&index=42\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n",[14,1158,1159,1170],{"__ignoreMap":35},[73,1160,1161,1163,1165,1168],{"class":75,"line":76},[73,1162,112],{"class":111},[73,1164,378],{"class":115},[73,1166,1167],{"class":87}," \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs?wait=5m&index=42\"",[73,1169,125],{"class":83},[73,1171,1172,1174,1176,1178],{"class":75,"line":128},[73,1173,131],{"class":115},[73,1175,274],{"class":87},[73,1177,277],{"class":160},[73,1179,280],{"class":87},[1181,1182,1183,1190],"ul",{},[1184,1185,1186,1189],"li",{},[14,1187,1188],{},"index=N"," — índice da última versão que você viu.",[1184,1191,1192,1195],{},[14,1193,1194],{},"wait=5m"," — tempo máximo de espera (máximo 10m).",[10,1197,1198],{},"Se nada mudar em 5 minutos, a resposta vem com o mesmo índice. Reusa-se o índice na próxima chamada.",[10,1200,1201],{},"Isso reduz tráfego e latência drasticamente em painéis ao vivo.",[22,1203,1205],{"id":1204},"rate-limiting","Rate limiting",[10,1207,1208,1209,1212],{},"Cada token tem um limite de ",[644,1210,1211],{},"1000 requisições por minuto",". Acima disso o servidor responde:",[27,1214,1216],{"className":67,"code":1215,"language":69,"meta":35,"style":35},"HTTP\u002F1.1 429 Too Many Requests\nRetry-After: 12\n",[14,1217,1218,1235],{"__ignoreMap":35},[73,1219,1220,1223,1226,1229,1232],{"class":75,"line":76},[73,1221,1222],{"class":83},"HTTP",[73,1224,1225],{"class":160},"\u002F",[73,1227,1228],{"class":115},"1.1",[73,1230,1231],{"class":115}," 429",[73,1233,1234],{"class":87}," Too Many Requests\n",[73,1236,1237,1240,1242],{"class":75,"line":128},[73,1238,1239],{"class":79},"Retry-After",[73,1241,84],{"class":83},[73,1243,1244],{"class":87}," 12\n",[10,1246,1247,1248,1250],{},"O header ",[14,1249,1239],{}," indica em segundos quando você pode tentar de novo.",[10,1252,1253],{},"Tokens admin têm limite separado e mais generoso. Para automação pesada, peça um token de serviço dedicado.",[22,1255,1257],{"id":1256},"codigos-de-erro","Códigos de erro",[293,1259,1260,1270],{},[296,1261,1262],{},[299,1263,1264,1267],{},[302,1265,1266],{},"Código",[302,1268,1269],{},"Significado",[312,1271,1272,1280,1288,1296,1303,1311,1319,1327,1335,1343],{},[299,1273,1274,1277],{},[317,1275,1276],{},"200",[317,1278,1279],{},"OK",[299,1281,1282,1285],{},[317,1283,1284],{},"201",[317,1286,1287],{},"Criado",[299,1289,1290,1293],{},[317,1291,1292],{},"400",[317,1294,1295],{},"Requisição inválida (JSON malformado, campo faltando)",[299,1297,1298,1300],{},[317,1299,94],{},[317,1301,1302],{},"Token ausente, expirado ou inválido",[299,1304,1305,1308],{},[317,1306,1307],{},"403",[317,1309,1310],{},"Token válido, mas sem permissão para a rota",[299,1312,1313,1316],{},[317,1314,1315],{},"404",[317,1317,1318],{},"Recurso não existe",[299,1320,1321,1324],{},[317,1322,1323],{},"409",[317,1325,1326],{},"Conflito (ex.: criar job com nome existente)",[299,1328,1329,1332],{},[317,1330,1331],{},"429",[317,1333,1334],{},"Rate limit atingido",[299,1336,1337,1340],{},[317,1338,1339],{},"500",[317,1341,1342],{},"Erro interno",[299,1344,1345,1348],{},[317,1346,1347],{},"503",[317,1349,1350],{},"Cluster temporariamente sem coordenação",[10,1352,1353],{},"Erros vêm com corpo padronizado:",[27,1355,1357],{"className":151,"code":1356,"language":153,"meta":35,"style":35},"{\n  \"error\": \"job_not_found\",\n  \"message\": \"job 'foo' não existe\",\n  \"request_id\": \"req_a1b2c3\"\n}\n",[14,1358,1359,1363,1375,1387,1397],{"__ignoreMap":35},[73,1360,1361],{"class":75,"line":76},[73,1362,161],{"class":160},[73,1364,1365,1368,1370,1373],{"class":75,"line":128},[73,1366,1367],{"class":79},"  \"error\"",[73,1369,169],{"class":160},[73,1371,1372],{"class":87},"\"job_not_found\"",[73,1374,175],{"class":160},[73,1376,1377,1380,1382,1385],{"class":75,"line":139},[73,1378,1379],{"class":79},"  \"message\"",[73,1381,169],{"class":160},[73,1383,1384],{"class":87},"\"job 'foo' não existe\"",[73,1386,175],{"class":160},[73,1388,1389,1392,1394],{"class":75,"line":190},[73,1390,1391],{"class":79},"  \"request_id\"",[73,1393,169],{"class":160},[73,1395,1396],{"class":87},"\"req_a1b2c3\"\n",[73,1398,1399],{"class":75,"line":201},[73,1400,204],{"class":160},[10,1402,1403,1404,1407],{},"Sempre inclua o ",[14,1405,1406],{},"request_id"," quando abrir um chamado de suporte.",[22,1409,1411],{"id":1410},"webhooks-plano-business","Webhooks (Plano Business)",[10,1413,1414],{},"Você registra uma URL e o cluster envia POSTs em eventos relevantes.",[27,1416,1418],{"className":102,"code":1417,"language":104,"meta":35,"style":35},"curl -X POST https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fwebhooks \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d '{\n    \"url\": \"https:\u002F\u002Fapi.minha-empresa.com\u002Fheroctl-events\",\n    \"events\": [\"job.deployed\", \"alloc.failed\", \"node.down\"],\n    \"secret\": \"whk_a1b2c3\"\n  }'\n",[14,1419,1420,1433,1445,1452,1457,1462,1468],{"__ignoreMap":35},[73,1421,1422,1424,1426,1428,1431],{"class":75,"line":76},[73,1423,112],{"class":111},[73,1425,116],{"class":115},[73,1427,119],{"class":87},[73,1429,1430],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fwebhooks",[73,1432,125],{"class":83},[73,1434,1435,1437,1439,1441,1443],{"class":75,"line":128},[73,1436,131],{"class":115},[73,1438,274],{"class":87},[73,1440,277],{"class":160},[73,1442,394],{"class":87},[73,1444,125],{"class":83},[73,1446,1447,1449],{"class":75,"line":139},[73,1448,142],{"class":115},[73,1450,1451],{"class":87}," '{\n",[73,1453,1454],{"class":75,"line":190},[73,1455,1456],{"class":87},"    \"url\": \"https:\u002F\u002Fapi.minha-empresa.com\u002Fheroctl-events\",\n",[73,1458,1459],{"class":75,"line":201},[73,1460,1461],{"class":87},"    \"events\": [\"job.deployed\", \"alloc.failed\", \"node.down\"],\n",[73,1463,1465],{"class":75,"line":1464},6,[73,1466,1467],{"class":87},"    \"secret\": \"whk_a1b2c3\"\n",[73,1469,1471],{"class":75,"line":1470},7,[73,1472,1473],{"class":87},"  }'\n",[10,1475,1476],{},"Eventos disponíveis:",[1181,1478,1479,1485,1491,1497,1503,1509,1515],{},[1184,1480,1481,1484],{},[14,1482,1483],{},"job.submitted"," — novo job enviado",[1184,1486,1487,1490],{},[14,1488,1489],{},"job.deployed"," — todas as réplicas saudáveis",[1184,1492,1493,1496],{},[14,1494,1495],{},"job.failed"," — deploy falhou e fez rollback",[1184,1498,1499,1502],{},[14,1500,1501],{},"alloc.failed"," — uma instância caiu",[1184,1504,1505,1508],{},[14,1506,1507],{},"node.down"," — um servidor saiu do cluster",[1184,1510,1511,1514],{},[14,1512,1513],{},"cert.renewed"," — certificado TLS renovado",[1184,1516,1517,1520],{},[14,1518,1519],{},"secret.changed"," — valor de secret atualizado",[10,1522,1523,1524,1527,1528,1531],{},"Cada POST traz um header ",[14,1525,1526],{},"X-HeroCtl-Signature"," com HMAC-SHA256 do corpo, usando o ",[14,1529,1530],{},"secret"," que você registrou. Valide antes de processar.",[22,1533,1535],{"id":1534},"proximos-passos","Próximos passos",[1181,1537,1538,1547,1554],{},[1184,1539,1540,1541,1546],{},"Veja ",[1542,1543,1545],"a",{"href":1544},"\u002Fdocs\u002Fseguranca\u002Frbac","autenticação e ACL"," para criar tokens com escopo restrito.",[1184,1548,1540,1549,1553],{},[1542,1550,1552],{"href":1551},"\u002Fdocs\u002Ftroubleshooting\u002Fproblemas-comuns","troubleshooting"," se uma chamada estiver retornando erros inesperados.",[1184,1555,1540,1556,1560],{},[1542,1557,1559],{"href":1558},"\u002Fdocs\u002Fobservabilidade\u002Fbackup-restore","backup e restore"," para proteger o estado que a API expõe.",[1562,1563,1564],"style",{},"html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sZEs4, html code.shiki .sZEs4{--shiki-default:#E6EDF3}html pre.shiki code .sH3jZ, html code.shiki .sH3jZ{--shiki-default:#8B949E}html pre.shiki code .sPWt5, html code.shiki .sPWt5{--shiki-default:#7EE787}",{"title":35,"searchDepth":128,"depth":128,"links":1566},[1567,1568,1572,1583,1584,1585,1586,1587],{"id":24,"depth":128,"text":25},{"id":60,"depth":128,"text":61,"children":1569},[1570,1571],{"id":98,"depth":139,"text":99},{"id":245,"depth":139,"text":246},{"id":283,"depth":128,"text":284,"children":1573},[1574,1575,1576,1577,1578,1579,1580,1581,1582],{"id":287,"depth":139,"text":288},{"id":453,"depth":139,"text":454},{"id":543,"depth":139,"text":544},{"id":650,"depth":139,"text":651},{"id":735,"depth":139,"text":736},{"id":850,"depth":139,"text":851},{"id":922,"depth":139,"text":923},{"id":1009,"depth":139,"text":1010},{"id":1074,"depth":139,"text":1075},{"id":1149,"depth":128,"text":1150},{"id":1204,"depth":128,"text":1205},{"id":1256,"depth":128,"text":1257},{"id":1410,"depth":128,"text":1411},{"id":1534,"depth":128,"text":1535},"api","Endpoints, autenticação JWT, exemplos com curl e padrões de erro da API do HeroCtl.",false,"md","i-lucide-code","2026-04-26",{},true,"\u002Fdocs\u002Fapi\u002Freferencia-api",[],"12 min",{"title":5,"description":1589},"docs\u002Fapi\u002Freferencia-api",[1588,1602,1603,112,1604],"rest","jwt","webhooks","gu0wXYDSF5BEmAMpyQLlKA_aqRGE5vj-HtaCrfwdNhg",[1607,1608,1614,1619,1624,1629,1635,1640,1645,1650,1656,1660,1665,1670],{"path":1596,"title":5,"description":1589,"category":1588,"order":76,"icon":1592},{"path":1609,"title":1610,"description":1611,"category":1612,"order":76,"icon":1613},"\u002Fdocs\u002Fdeploy\u002Fprimeiro-deploy","Deploy do primeiro app","Suba uma aplicação Node.js com banco Postgres em 50 linhas de YAML. Inclui health check, rolling deploy e rollback.","deploy","i-lucide-rocket",{"path":1615,"title":1616,"description":1617,"category":1612,"order":128,"icon":1618},"\u002Fdocs\u002Fdeploy\u002Frolling-canary-bluegreen","Rolling, canary, blue-green e rainbow","Quatro estratégias de deploy. Quando usar cada uma, com exemplos completos e trade-offs honestos.","i-lucide-git-branch",{"path":1558,"title":1620,"description":1621,"category":1622,"order":128,"icon":1623},"Backup e restore do estado do cluster","Como salvar, agendar e restaurar snapshots do plano de controle do HeroCtl. Estratégia de disaster recovery.","observabilidade","i-lucide-archive",{"path":1625,"title":1626,"description":1627,"category":1622,"order":76,"icon":1628},"\u002Fdocs\u002Fobservabilidade\u002Fmetricas-logs","Métricas e logs","Coleta de métricas, logs e traces sem montar uma pilha de observabilidade externa. Quando vale, e quando integrar com ferramenta de fora.","i-lucide-activity",{"path":1630,"title":1631,"description":1632,"category":1633,"order":139,"icon":1634},"\u002Fdocs\u002Foperacoes\u002Fcomandos-cli","Referência completa do CLI","Todos os comandos heroctl com sinopse, flags e exemplo. Use como cola de mesa.","operacoes","i-lucide-terminal",{"path":1636,"title":1637,"description":1638,"category":1633,"order":76,"icon":1639},"\u002Fdocs\u002Foperacoes\u002Finstalacao","Instalação","Instale o HeroCtl em qualquer servidor Linux com Docker em um único comando. Cobre pré-requisitos, bootstrap e verificação.","i-lucide-download",{"path":1641,"title":1642,"description":1643,"category":1633,"order":190,"icon":1644},"\u002Fdocs\u002Foperacoes\u002Fmulti-region","Multi-region (em planejamento Q4 2026)","O que esperar de multi-region no HeroCtl, como rodar em várias regiões hoje e o roadmap até 2027.","i-lucide-globe",{"path":1646,"title":1647,"description":1648,"category":1633,"order":128,"icon":1649},"\u002Fdocs\u002Foperacoes\u002Fprimeiro-cluster","Subir cluster de 3 nós","Forme um cluster com 3 servidores em menos de 10 minutos. Tolera falha de 1 nó sem indisponibilidade.","i-lucide-network",{"path":1651,"title":1652,"description":1653,"category":1654,"order":128,"icon":1655},"\u002Fdocs\u002Frede\u002Ffirewall","Configuração de firewall","Quais portas o HeroCtl usa, quais precisam ficar abertas, e quais nunca deveriam ser expostas à internet.","rede","i-lucide-shield",{"path":1657,"title":1658,"description":1659,"category":1654,"order":76,"icon":1644},"\u002Fdocs\u002Frede\u002Fingress-tls","Ingress e TLS automático","Como expor aplicações pela porta 443 com certificados emitidos e renovados automaticamente, sem operar um roteador externo.",{"path":1544,"title":1661,"description":1662,"category":1663,"order":128,"icon":1664},"RBAC e controle de acesso (Business+)","Modelo de papéis, políticas e tokens para limitar quem pode submeter, ler e operar o cluster.","seguranca","i-lucide-users",{"path":1666,"title":1667,"description":1668,"category":1663,"order":76,"icon":1669},"\u002Fdocs\u002Fseguranca\u002Fsecrets","Gerenciamento de segredos","Como guardar senhas, tokens e chaves fora do spec do job, com criptografia em repouso e rotação versionada.","i-lucide-key",{"path":1551,"title":1671,"description":1672,"category":1552,"order":76,"icon":1673},"Troubleshooting de problemas comuns","Os 12 problemas mais frequentes em clusters HeroCtl, com sintoma, diagnóstico e correção passo a passo.","i-lucide-alert-triangle",1777362179472]