[{"data":1,"prerenderedAt":1675},["ShallowReactive",2],{"doc-en-\u002Fen\u002Fdocs\u002Fapi\u002Fapi-reference":3,"docs-en-all":1607},{"id":4,"title":5,"body":6,"category":1589,"description":1590,"draft":1591,"extension":1592,"icon":1593,"lastReviewed":1594,"meta":1595,"navigation":1596,"order":76,"path":1597,"prerequisites":1598,"readingTime":1599,"seo":1600,"stem":1601,"tags":1602,"__hash__":1606},"docs_en\u002Fen\u002Fdocs\u002Fapi\u002Fapi-reference.md","REST API reference",{"type":7,"value":8,"toc":1566},"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,1002,1008,1012,1015,1053,1073,1077,1080,1118,1148,1152,1155,1181,1197,1200,1203,1207,1214,1246,1252,1255,1259,1352,1355,1402,1409,1413,1416,1475,1478,1522,1533,1537,1562],[10,11,12,13,17],"p",{},"The HeroCtl API is the single channel between you, the CLI, and the web interface. Everything the panel shows passes through here. Everything ",[14,15,16],"code",{},"heroctl"," does on the command line too.",[10,19,20],{},"The API is REST. JSON in, JSON out. No GraphQL. No public gRPC.",[22,23,25],"h2",{"id":24},"base-endpoint","Base endpoint",[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],{},"In standard production: ",[14,40,41],{},"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002F",".",[10,44,45,46,49,50,53,54,57],{},"All routes live under ",[14,47,48],{},"\u002Fv1\u002F",". When ",[14,51,52],{},"v2"," arrives, ",[14,55,56],{},"v1"," keeps responding for at least 12 months.",[22,59,61],{"id":60},"authentication","Authentication",[10,63,64],{},"The API uses JWT. You obtain a token via login and send it on every request:",[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],{},"No header, no response. The server replies ",[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],{},"Response:",[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],{},"The default token lasts 24 hours.",[10,209,210],{},"You can also authenticate with an ACL token (generated through the interface or the 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],{},"Before the token expires, swap it for a new one:",[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},"main-endpoints","Main endpoints",[96,286,288],{"id":287},"jobs","Jobs",[10,290,291],{},"A job is the declarative definition of what should run. The cluster takes care of the rest.",[293,294,295,311],"table",{},[296,297,298],"thead",{},[299,300,301,305,308],"tr",{},[302,303,304],"th",{},"Method",[302,306,307],{},"Route",[302,309,310],{},"What it does",[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],{},"Lists all jobs",[299,329,330,332,337],{},[317,331,319],{},[317,333,334],{},[14,335,336],{},"\u002Fv1\u002Fjobs\u002F:name",[317,338,339],{},"Detail of a job",[299,341,342,345,349],{},[317,343,344],{},"POST",[317,346,347],{},[14,348,324],{},[317,350,351],{},"Submits or updates a job",[299,353,354,357,361],{},[317,355,356],{},"DELETE",[317,358,359],{},[14,360,336],{},[317,362,363],{},"Removes the job and its instances",[10,365,366],{},"List 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],{},"Submit a 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],{},"Each replica of a job becomes an allocation. It's the actual unit of execution.",[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],{},"Lists allocations",[299,485,486,488,493],{},[317,487,319],{},[317,489,490],{},[14,491,492],{},"\u002Fv1\u002Fallocations\u002F:id",[317,494,495],{},"Detail",[299,497,498,500,505],{},[317,499,344],{},[317,501,502],{},[14,503,504],{},"\u002Fv1\u002Fallocations\u002F:id\u002Fstop",[317,506,507],{},"Stops an 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],{},"The servers that run 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],{},"Lists nodes",[299,575,576,578,583],{},[317,577,319],{},[317,579,580],{},[14,581,582],{},"\u002Fv1\u002Fnodes\u002F:id",[317,584,585],{},"Detail (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],{},"Drains a node (moves workloads off)",[10,599,600],{},"Drain a node before maintenance:",[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",{},"Warning:"," drain removes all allocations from the node. Make sure capacity exists on the others first.",[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],{},"Overall health, coordinator node, member count",[299,679,680,682,687],{},[317,681,319],{},[317,683,684],{},[14,685,686],{},"\u002Fv1\u002Fcluster\u002Fpeers",[317,688,689],{},"List of control plane peers",[299,691,692,694,699],{},[317,693,344],{},[317,695,696],{},[14,697,698],{},"\u002Fv1\u002Fraft\u002Ftransfer-leadership",[317,700,701],{},"Transfers coordination (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],{},"Sensitive values. Stored encrypted at rest.",[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],{},"Lists names (never values)",[299,767,768,770,774],{},[317,769,344],{},[317,771,772],{},[14,773,762],{},[317,775,776],{},"Creates or updates",[299,778,779,781,786],{},[317,780,319],{},[317,782,783],{},[14,784,785],{},"\u002Fv1\u002Fsecrets\u002F:name",[317,787,788],{},"Reads a value (requires specific ACL)",[299,790,791,793,797],{},[317,792,356],{},[317,794,795],{},[14,796,785],{},[317,798,799],{},"Deletes",[10,801,802],{},"Create a 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},"metrics","Metrics",[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],{},"Current snapshot (CPU, mem, allocs, ingress)",[299,879,880,882,887],{},[317,881,319],{},[317,883,884],{},[14,885,886],{},"\u002Fv1\u002Fmetrics\u002Fquery",[317,888,889],{},"Historical query with time window",[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 live per 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],{},"Last N lines",[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],{},"Live streaming:",[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,1001],{},"The output comes in ",[14,999,1000],{},"text\u002Fevent-stream"," format:",[27,1003,1006],{"className":1004,"code":1005,"language":32},[30],"event: log\ndata: {\"alloc\":\"a1b2\",\"line\":\"server started\"}\n",[14,1007,1005],{"__ignoreMap":35},[96,1009,1011],{"id":1010},"health","Health",[10,1013,1014],{},"Endpoints without authentication. For external monitoring use.",[293,1016,1017,1027],{},[296,1018,1019],{},[299,1020,1021,1023,1025],{},[302,1022,304],{},[302,1024,307],{},[302,1026,310],{},[312,1028,1029,1041],{},[299,1030,1031,1033,1038],{},[317,1032,319],{},[317,1034,1035],{},[14,1036,1037],{},"\u002Fv1\u002Fhealth\u002Fready",[317,1039,1040],{},"Ready to accept traffic",[299,1042,1043,1045,1050],{},[317,1044,319],{},[317,1046,1047],{},[14,1048,1049],{},"\u002Fv1\u002Fhealth\u002Flive",[317,1051,1052],{},"Process alive",[27,1054,1056],{"className":102,"code":1055,"language":104,"meta":35,"style":35},"curl -s https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fhealth\u002Fready\n# {\"status\":\"ok\"}\n",[14,1057,1058,1067],{"__ignoreMap":35},[73,1059,1060,1062,1064],{"class":75,"line":76},[73,1061,112],{"class":111},[73,1063,378],{"class":115},[73,1065,1066],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fhealth\u002Fready\n",[73,1068,1069],{"class":75,"line":128},[73,1070,1072],{"class":1071},"sH3jZ","# {\"status\":\"ok\"}\n",[96,1074,1076],{"id":1075},"ingress","Ingress",[10,1078,1079],{},"Domains served by the cluster.",[293,1081,1082,1092],{},[296,1083,1084],{},[299,1085,1086,1088,1090],{},[302,1087,304],{},[302,1089,307],{},[302,1091,310],{},[312,1093,1094,1106],{},[299,1095,1096,1098,1103],{},[317,1097,319],{},[317,1099,1100],{},[14,1101,1102],{},"\u002Fv1\u002Fingress",[317,1104,1105],{},"All active domains",[299,1107,1108,1110,1115],{},[317,1109,319],{},[317,1111,1112],{},[14,1113,1114],{},"\u002Fv1\u002Fingress\u002F:host",[317,1116,1117],{},"Detail of a domain (cert, routes, target job)",[27,1119,1121],{"className":102,"code":1120,"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,1122,1123,1134],{"__ignoreMap":35},[73,1124,1125,1127,1129,1132],{"class":75,"line":76},[73,1126,112],{"class":111},[73,1128,378],{"class":115},[73,1130,1131],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fingress\u002Fheroctl.com",[73,1133,125],{"class":83},[73,1135,1136,1138,1140,1142,1144,1146],{"class":75,"line":128},[73,1137,131],{"class":115},[73,1139,274],{"class":87},[73,1141,277],{"class":160},[73,1143,394],{"class":87},[73,1145,397],{"class":83},[73,1147,732],{"class":111},[22,1149,1151],{"id":1150},"long-polling","Long polling",[10,1153,1154],{},"Several endpoints support blocking waits. Instead of polling every second, you pass an index and a maximum wait time. The response returns when something changes — or when the time runs out.",[27,1156,1158],{"className":102,"code":1157,"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,1159,1160,1171],{"__ignoreMap":35},[73,1161,1162,1164,1166,1169],{"class":75,"line":76},[73,1163,112],{"class":111},[73,1165,378],{"class":115},[73,1167,1168],{"class":87}," \"https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fjobs?wait=5m&index=42\"",[73,1170,125],{"class":83},[73,1172,1173,1175,1177,1179],{"class":75,"line":128},[73,1174,131],{"class":115},[73,1176,274],{"class":87},[73,1178,277],{"class":160},[73,1180,280],{"class":87},[1182,1183,1184,1191],"ul",{},[1185,1186,1187,1190],"li",{},[14,1188,1189],{},"index=N"," — index of the last version you saw.",[1185,1192,1193,1196],{},[14,1194,1195],{},"wait=5m"," — max wait time (max 10m).",[10,1198,1199],{},"If nothing changes in 5 minutes, the response comes back with the same index. Reuse the index in the next call.",[10,1201,1202],{},"This dramatically reduces traffic and latency on live panels.",[22,1204,1206],{"id":1205},"rate-limiting","Rate limiting",[10,1208,1209,1210,1213],{},"Each token has a limit of ",[644,1211,1212],{},"1000 requests per minute",". Above that the server replies:",[27,1215,1217],{"className":67,"code":1216,"language":69,"meta":35,"style":35},"HTTP\u002F1.1 429 Too Many Requests\nRetry-After: 12\n",[14,1218,1219,1236],{"__ignoreMap":35},[73,1220,1221,1224,1227,1230,1233],{"class":75,"line":76},[73,1222,1223],{"class":83},"HTTP",[73,1225,1226],{"class":160},"\u002F",[73,1228,1229],{"class":115},"1.1",[73,1231,1232],{"class":115}," 429",[73,1234,1235],{"class":87}," Too Many Requests\n",[73,1237,1238,1241,1243],{"class":75,"line":128},[73,1239,1240],{"class":79},"Retry-After",[73,1242,84],{"class":83},[73,1244,1245],{"class":87}," 12\n",[10,1247,1248,1249,1251],{},"The ",[14,1250,1240],{}," header indicates in seconds when you can try again.",[10,1253,1254],{},"Admin tokens have a separate, more generous limit. For heavy automation, request a dedicated service token.",[22,1256,1258],{"id":1257},"error-codes","Error codes",[293,1260,1261,1271],{},[296,1262,1263],{},[299,1264,1265,1268],{},[302,1266,1267],{},"Status code",[302,1269,1270],{},"Meaning",[312,1272,1273,1281,1289,1297,1304,1312,1320,1328,1336,1344],{},[299,1274,1275,1278],{},[317,1276,1277],{},"200",[317,1279,1280],{},"OK",[299,1282,1283,1286],{},[317,1284,1285],{},"201",[317,1287,1288],{},"Created",[299,1290,1291,1294],{},[317,1292,1293],{},"400",[317,1295,1296],{},"Invalid request (malformed JSON, missing field)",[299,1298,1299,1301],{},[317,1300,94],{},[317,1302,1303],{},"Token missing, expired, or invalid",[299,1305,1306,1309],{},[317,1307,1308],{},"403",[317,1310,1311],{},"Token valid, but no permission for the route",[299,1313,1314,1317],{},[317,1315,1316],{},"404",[317,1318,1319],{},"Resource does not exist",[299,1321,1322,1325],{},[317,1323,1324],{},"409",[317,1326,1327],{},"Conflict (e.g., creating a job with an existing name)",[299,1329,1330,1333],{},[317,1331,1332],{},"429",[317,1334,1335],{},"Rate limit hit",[299,1337,1338,1341],{},[317,1339,1340],{},"500",[317,1342,1343],{},"Internal error",[299,1345,1346,1349],{},[317,1347,1348],{},"503",[317,1350,1351],{},"Cluster temporarily without coordination",[10,1353,1354],{},"Errors come with a standardized body:",[27,1356,1358],{"className":151,"code":1357,"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,1359,1360,1364,1376,1388,1398],{"__ignoreMap":35},[73,1361,1362],{"class":75,"line":76},[73,1363,161],{"class":160},[73,1365,1366,1369,1371,1374],{"class":75,"line":128},[73,1367,1368],{"class":79},"  \"error\"",[73,1370,169],{"class":160},[73,1372,1373],{"class":87},"\"job_not_found\"",[73,1375,175],{"class":160},[73,1377,1378,1381,1383,1386],{"class":75,"line":139},[73,1379,1380],{"class":79},"  \"message\"",[73,1382,169],{"class":160},[73,1384,1385],{"class":87},"\"job 'foo' não existe\"",[73,1387,175],{"class":160},[73,1389,1390,1393,1395],{"class":75,"line":190},[73,1391,1392],{"class":79},"  \"request_id\"",[73,1394,169],{"class":160},[73,1396,1397],{"class":87},"\"req_a1b2c3\"\n",[73,1399,1400],{"class":75,"line":201},[73,1401,204],{"class":160},[10,1403,1404,1405,1408],{},"Always include the ",[14,1406,1407],{},"request_id"," when you open a support ticket.",[22,1410,1412],{"id":1411},"webhooks-business-plan","Webhooks (Business plan)",[10,1414,1415],{},"You register a URL and the cluster sends POSTs on relevant events.",[27,1417,1419],{"className":102,"code":1418,"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,1420,1421,1434,1446,1453,1458,1463,1469],{"__ignoreMap":35},[73,1422,1423,1425,1427,1429,1432],{"class":75,"line":76},[73,1424,112],{"class":111},[73,1426,116],{"class":115},[73,1428,119],{"class":87},[73,1430,1431],{"class":87}," https:\u002F\u002Fmanage.heroctl.com\u002Fv1\u002Fwebhooks",[73,1433,125],{"class":83},[73,1435,1436,1438,1440,1442,1444],{"class":75,"line":128},[73,1437,131],{"class":115},[73,1439,274],{"class":87},[73,1441,277],{"class":160},[73,1443,394],{"class":87},[73,1445,125],{"class":83},[73,1447,1448,1450],{"class":75,"line":139},[73,1449,142],{"class":115},[73,1451,1452],{"class":87}," '{\n",[73,1454,1455],{"class":75,"line":190},[73,1456,1457],{"class":87},"    \"url\": \"https:\u002F\u002Fapi.minha-empresa.com\u002Fheroctl-events\",\n",[73,1459,1460],{"class":75,"line":201},[73,1461,1462],{"class":87},"    \"events\": [\"job.deployed\", \"alloc.failed\", \"node.down\"],\n",[73,1464,1466],{"class":75,"line":1465},6,[73,1467,1468],{"class":87},"    \"secret\": \"whk_a1b2c3\"\n",[73,1470,1472],{"class":75,"line":1471},7,[73,1473,1474],{"class":87},"  }'\n",[10,1476,1477],{},"Available events:",[1182,1479,1480,1486,1492,1498,1504,1510,1516],{},[1185,1481,1482,1485],{},[14,1483,1484],{},"job.submitted"," — new job submitted",[1185,1487,1488,1491],{},[14,1489,1490],{},"job.deployed"," — all replicas healthy",[1185,1493,1494,1497],{},[14,1495,1496],{},"job.failed"," — deploy failed and rolled back",[1185,1499,1500,1503],{},[14,1501,1502],{},"alloc.failed"," — an instance went down",[1185,1505,1506,1509],{},[14,1507,1508],{},"node.down"," — a server left the cluster",[1185,1511,1512,1515],{},[14,1513,1514],{},"cert.renewed"," — TLS certificate renewed",[1185,1517,1518,1521],{},[14,1519,1520],{},"secret.changed"," — secret value updated",[10,1523,1524,1525,1528,1529,1532],{},"Each POST carries an ",[14,1526,1527],{},"X-HeroCtl-Signature"," header with HMAC-SHA256 of the body, using the ",[14,1530,1531],{},"secret"," you registered. Validate before processing.",[22,1534,1536],{"id":1535},"next-steps","Next steps",[1182,1538,1539,1548,1555],{},[1185,1540,1541,1542,1547],{},"See ",[1543,1544,1546],"a",{"href":1545},"\u002Fen\u002Fdocs\u002Fsecurity\u002Frbac","authentication and ACL"," to create scoped tokens.",[1185,1549,1541,1550,1554],{},[1543,1551,1553],{"href":1552},"\u002Fen\u002Fdocs\u002Ftroubleshooting\u002Fcommon-problems","troubleshooting"," if a call returns unexpected errors.",[1185,1556,1541,1557,1561],{},[1543,1558,1560],{"href":1559},"\u002Fen\u002Fdocs\u002Fobservability\u002Fbackup-restore","backup and restore"," to protect the state the API exposes.",[1563,1564,1565],"style",{},"html pre.shiki code .sPWt5, html code.shiki .sPWt5{--shiki-default:#7EE787}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}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 .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .sZEs4, html code.shiki .sZEs4{--shiki-default:#E6EDF3}html pre.shiki code .sH3jZ, html code.shiki .sH3jZ{--shiki-default:#8B949E}",{"title":35,"searchDepth":128,"depth":128,"links":1567},[1568,1569,1573,1584,1585,1586,1587,1588],{"id":24,"depth":128,"text":25},{"id":60,"depth":128,"text":61,"children":1570},[1571,1572],{"id":98,"depth":139,"text":99},{"id":245,"depth":139,"text":246},{"id":283,"depth":128,"text":284,"children":1574},[1575,1576,1577,1578,1579,1580,1581,1582,1583],{"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":1010,"depth":139,"text":1011},{"id":1075,"depth":139,"text":1076},{"id":1150,"depth":128,"text":1151},{"id":1205,"depth":128,"text":1206},{"id":1257,"depth":128,"text":1258},{"id":1411,"depth":128,"text":1412},{"id":1535,"depth":128,"text":1536},"api","Endpoints, JWT authentication, curl examples, and error patterns of the HeroCtl API.",false,"md","i-lucide-code","2026-04-26",{},true,"\u002Fen\u002Fdocs\u002Fapi\u002Fapi-reference",[],"12 min read",{"title":5,"description":1590},"en\u002Fdocs\u002Fapi\u002Fapi-reference",[1589,1603,1604,112,1605],"rest","jwt","webhooks","d5w8I6pVLCLo0DVUnupq7eKQcorUXckXnQDo2o1s8_E",[1608,1609,1615,1620,1626,1631,1636,1641,1647,1652,1657,1661,1666,1671],{"path":1597,"title":5,"description":1590,"category":1589,"order":76,"icon":1593},{"path":1610,"title":1611,"description":1612,"category":1613,"order":76,"icon":1614},"\u002Fen\u002Fdocs\u002Fdeploy\u002Ffirst-deploy","Deploy your first app","Bring up a Node.js application with a Postgres database in 50 lines of YAML. Includes health check, rolling deploy, and rollback.","deploy","i-lucide-rocket",{"path":1616,"title":1617,"description":1618,"category":1613,"order":128,"icon":1619},"\u002Fen\u002Fdocs\u002Fdeploy\u002Frolling-canary-blue-green","Rolling, canary, blue-green, and rainbow","Four deploy strategies. When to use each, with complete examples and honest trade-offs.","i-lucide-git-branch",{"path":1621,"title":1622,"description":1623,"category":1624,"order":128,"icon":1625},"\u002Fen\u002Fdocs\u002Fnetworking\u002Ffirewall","Firewall configuration","Which ports HeroCtl uses, which need to stay open, and which should never be exposed to the internet.","rede","i-lucide-shield",{"path":1627,"title":1628,"description":1629,"category":1624,"order":76,"icon":1630},"\u002Fen\u002Fdocs\u002Fnetworking\u002Fingress-tls","Ingress and automatic TLS","How to expose applications on port 443 with certificates issued and renewed automatically, without operating an external router.","i-lucide-globe",{"path":1559,"title":1632,"description":1633,"category":1634,"order":128,"icon":1635},"Backup and restore of cluster state","How to save, schedule, and restore HeroCtl control plane snapshots. Disaster recovery strategy.","observabilidade","i-lucide-archive",{"path":1637,"title":1638,"description":1639,"category":1634,"order":76,"icon":1640},"\u002Fen\u002Fdocs\u002Fobservability\u002Fmetrics-logs","Metrics and logs","Collect metrics, logs, and traces without standing up an external observability stack. When it's worth it, and when to integrate with an outside tool.","i-lucide-activity",{"path":1642,"title":1643,"description":1644,"category":1645,"order":139,"icon":1646},"\u002Fen\u002Fdocs\u002Foperations\u002Fcli-reference","Complete CLI reference","All heroctl commands with synopsis, flags, and example. Use as a desk reference.","operacoes","i-lucide-terminal",{"path":1648,"title":1649,"description":1650,"category":1645,"order":128,"icon":1651},"\u002Fen\u002Fdocs\u002Foperations\u002Ffirst-cluster","Bring up a 3-node cluster","Form a cluster with 3 servers in under 10 minutes. Tolerates 1-node failure with no downtime.","i-lucide-network",{"path":1653,"title":1654,"description":1655,"category":1645,"order":76,"icon":1656},"\u002Fen\u002Fdocs\u002Foperations\u002Finstallation","Installation","Install HeroCtl on any Linux server with Docker in a single command. Covers prerequisites, bootstrap, and verification.","i-lucide-download",{"path":1658,"title":1659,"description":1660,"category":1645,"order":190,"icon":1630},"\u002Fen\u002Fdocs\u002Foperations\u002Fmulti-region","Multi-region (planned for Q4 2026)","What to expect from multi-region in HeroCtl, how to run across regions today, and the roadmap through 2027.",{"path":1545,"title":1662,"description":1663,"category":1664,"order":128,"icon":1665},"RBAC and access control (Business+)","Role, policy, and token model to limit who can submit, read, and operate the cluster.","seguranca","i-lucide-users",{"path":1667,"title":1668,"description":1669,"category":1664,"order":76,"icon":1670},"\u002Fen\u002Fdocs\u002Fsecurity\u002Fsecrets","Secret management","How to keep passwords, tokens, and keys outside the job spec, with encryption at rest and versioned rotation.","i-lucide-key",{"path":1552,"title":1672,"description":1673,"category":1553,"order":76,"icon":1674},"Troubleshooting common problems","The 12 most frequent problems in HeroCtl clusters, with symptom, diagnosis, and step-by-step fix.","i-lucide-alert-triangle",1777362181910]