CRON
Jobs agendados
Agende tarefas recorrentes que rodam isoladas em containers Docker: sua própria imagem, ou um repositório Git que a gente builda. Você define o quê e quando; a execução acontece fora da chamada que criou o job.
Scheduled jobs
Schedule recurring tasks that run isolated in Docker containers: your own image, or a Git repo we build for you. You define what and when; execution happens outside the call that created the job.
ASYNC a resposta confirma, não executa the response confirms, it doesn't run the job
Por que é assíncrona
Why it's async
Nas outras APIs, a resposta HTTP é o resultado: você pergunta o endereço de um CEP, a resposta já é o endereço. Aqui não: quando você faz POST /api/v1/cron/jobs, a resposta é só a confirmação de que o job foi cadastrado, com um id e um next_run_at calculado a partir do schedule. O trabalho em si (buildar a imagem se for um repo Git, subir o container, rodar o comando) acontece depois, de forma independente, num worker separado que varre os jobs prontos pra rodar. Pra saber o que aconteceu numa execução, você consulta o job de volta (GET) ou olha o status do worker. A criação não devolve output nenhum, porque nada rodou ainda.
In the other APIs, the HTTP response is the result: you ask for a CEP's address, the response already is the address. Not here: when you POST /api/v1/cron/jobs, the response is just confirmation that the job was registered, with an id and a next_run_at computed from the schedule. The actual work (building the image if it's a Git repo, starting the container, running the command) happens later, independently, in a separate worker that scans for jobs ready to run. To find out what happened in a run, you query the job back (GET) or check the worker's status. Creation returns no output, because nothing has run yet.
Endpoints
Endpoints
/api/v1/cron/jobs
cria um jobcreates a job
/api/v1/cron/jobs
lista seus jobslists your jobs
/api/v1/cron/jobs/{id}
detalhe de um joba single job's detail
/api/v1/cron/jobs/{id}
atualiza um jobupdates a job
/api/v1/cron/jobs/{id}
remove um jobdeletes a job
/api/v1/cron/jobs/{id}/trigger
força uma execução imediataforces an immediate run
/api/v1/cron/worker/status
status do worker de execuçãoexecution worker's status
/api/v1/cron/worker/stop
pausa o worker, adminpauses the worker, admin only
/api/v1/cron/worker/start
retoma o worker, adminresumes the worker, admin only
worker/stop e worker/start exigem conta com role admin. Todo o resto da API é por conta de qualquer cliente autenticado, só esses dois controlam o processo que executa job de todo mundo.
worker/stop and worker/start require an admin-role account. Everything else is available to any authenticated client, but only these two control the process that runs everyone's jobs.
Campos do job
Job fields
| Campo | Field | Descrição | Description |
|---|---|---|---|
name | nome do job | the job's name | |
schedule | expressão cron ("0 3 * * *" = todo dia às 3h) | a cron expression ("0 3 * * *" = every day at 3am) | |
image_type | "image" (imagem Docker pronta) ou "git" (repositório: buildamos por você) | "image" (ready-made Docker image) or "git" (a repo: we build it for you) | |
image_source | nome:tag da imagem, ou a URL do repositório Git (ver seção abaixo) | the image name:tag, or the Git repo URL (see section below) | |
command | opcional: argumentos passados pro entrypoint da imagem, separados por espaço. Não passa por shell nenhum: sem pipe, sem &&, sem aspas com espaço dentro. Pra lógica mais complexa, coloque num script dentro da própria imagem e chame ele. | optional: arguments passed to the image's entrypoint, split on whitespace. Not run through a shell: no pipes, no &&, no quoted arguments with spaces inside. For anything more complex, put a script in the image itself and call that. | |
env_vars | opcional: variáveis de ambiente do container | optional: the container's environment variables | |
cpu_limit | opcional, padrão "0.5": núcleos de CPU (aceita fração, ex. "0.5", "2") | optional, defaults to "0.5": CPU cores (fractional allowed, e.g. "0.5", "2") | |
memory_limit | opcional, padrão "256m": formato Docker ("512m", "1g") | optional, defaults to "256m": Docker format ("512m", "1g") | |
timeout_sec | opcional, padrão 3600 (1h), máximo 86400 (24h): mata o container se ele passar disso. Só cobre o docker run; num job git, o clone + build não têm timeout próprio (ver seção abaixo) | optional, defaults to 3600 (1h), max 86400 (24h): kills the container past that point. Only covers the docker run step; on a git job, the clone + build have no timeout of their own (see section below) | |
missed_runs | "skip" (padrão) ou "run_once": se o worker estava fora do ar na hora agendada, run_once dispara uma execução assim que ele volta (checado no boot e a cada reload); skip só espera o próximo horário do schedule | "skip" (default) or "run_once": if the worker was down at the scheduled time, run_once fires one run as soon as it's back (checked on boot and on every reload); skip just waits for the schedule's next occurrence | |
allow_concurrent | padrão false: com false, uma execução que ainda está rodando faz o worker pular silenciosamente a próxima (sem erro, sem retry: só não roda, e não tem como ver isso além do log do worker) | defaults to false: with false, a run still in progress makes the worker silently skip the next one (no error, no retry: it just doesn't run, and there's no way to see that besides the worker's own log) |
Exemplo
Example
Exemplo de verdade, não só ilustrativo: github.com/alicercelabs/cron-example é um repositório público com um Dockerfile na raiz. Clona, builda e roda igual a qualquer repositório seu. Testado localmente (git clone + docker build + docker run) antes de entrar nessa página.
A real example, not just illustrative: github.com/alicercelabs/cron-example is a public repo with a Dockerfile at the root. Clones, builds and runs exactly like any repo of your own. Tested locally (git clone + docker build + docker run) before going on this page.
# criar: devolve o job com id, não o resultado de rodar nada# create: returns the job with an id, not the result of running anything
curl -X POST https://api.alicercelabs.com.br/api/v1/cron/jobs \
-H "Authorization: Bearer <token>" \
-d '{
"name": "relatorio-diario",
"schedule": "0 3 * * *",
"image_type": "git",
"image_source": "https://github.com/alicercelabs/cron-example",
"env_vars": { "REPORT_NAME": "vendas-agosto" },
"timeout_sec": 120
}'
{
"success": true,
"data": {
"id": "8f1c...",
"name": "relatorio-diario",
"schedule": "0 3 * * *",
"image_type": "git",
"image_source": "https://github.com/alicercelabs/cron-example",
"env_vars": { "REPORT_NAME": "vendas-agosto" },
"missed_runs": "skip",
"allow_concurrent": false,
"active": true,
"last_run_at": null,
"next_run_at": "2026-08-20T03:00:00Z",
"created_at": "2026-08-19T18:00:00Z"
},
"meta": { "elapsed_ms": 9, "request_id": "..." }
}
Repare em last_run_at: null: o job existe, mas ainda não rodou. Pra forçar uma execução sem esperar o schedule, use POST /jobs/{id}/trigger; pra ver o que aconteceu depois, consulte GET /jobs/{id} de novo e olhe last_run_at.
Note last_run_at: null: the job exists, but hasn't run yet. To force a run without waiting for the schedule, use POST /jobs/{id}/trigger; to see what happened afterward, query GET /jobs/{id} again and check last_run_at.
Imagem pronta ou repositório Git
Ready-made image or Git repo
Com image_type: "image", o worker roda docker pull no valor de image_source antes de cada execução, igual a rodar essa imagem na sua própria máquina: precisa estar em algum registry acessível (Docker Hub, GHCR, etc.) e ser pública, já que não há como configurar credencial de registry hoje.
With image_type: "image", the worker runs docker pull on image_source before every run, same as pulling that image yourself: needs to sit on some reachable registry (Docker Hub, GHCR, etc.) and be public, since there's no way to configure registry credentials today.
Com image_type: "git", image_source é a URL do repositório e o worker faz o trabalho: git clone --depth=1 seguido de docker build na raiz do clone. Três coisas que isso exige, na prática:
With image_type: "git", image_source is the repo's URL and the worker does the rest: git clone --depth=1 followed by docker build at the root of the clone. Three things that requires, in practice:
- o repositório precisa ser público: o clone não usa nenhuma credencial;
- tem que ter um
Dockerfilena raiz, não numa subpasta; - a imagem é rebuildada automaticamente sempre que você dá
PUTno job (senão, o worker reaproveita a imagem já buildada da execução anterior, sem clonar de novo).
- the repo has to be public: the clone doesn't use any credentials;
- it needs a
Dockerfileat the root, not in a subfolder; - the image gets rebuilt automatically whenever you
PUTthe job (otherwise the worker reuses the image built on the previous run, without re-cloning).
timeout_sec só limita o docker run do container em si. O git clone e o docker build de um job git rodam sem limite de tempo próprio. Um repositório com build lento ou travado não é morto por timeout_sec; ele só afeta depois que a imagem já existe.
timeout_sec only bounds the container's own docker run. The git clone and docker build steps of a git job run with no timeout of their own. A repo with a slow or hung build isn't killed by timeout_sec; that only kicks in once the image already exists.
Exemplo funcionando de ponta a ponta: github.com/alicercelabs/cron-example.
A working end-to-end example: github.com/alicercelabs/cron-example.
Erros possíveis
Possible errors
| Status | Motivo | Reason |
|---|---|---|
| 400 | campos obrigatórios ausentes, image_type inválido, schedule mal formado, ou image_source do tipo git sem http(s):// | missing required fields, invalid image_type, malformed schedule, or a git-type image_source without http(s):// |
| 401 | token ausente ou inválido | missing or invalid token |
| 403 | worker/stop ou worker/start chamado por conta que não é admin | worker/stop or worker/start called by a non-admin account |
| 404 | job não encontrado, ou não pertence a você | job not found, or it isn't yours |
| 429 | limite de taxa excedido: cada operação (create, list, trigger...) tem cota própria | rate limit exceeded: each operation (create, list, trigger...) has its own quota |
Usando o SDK
Using the SDK
CRUD completo do job, mais controle do daemon que executa os jobs. Os campos aceitos em create/update estão na seção Campos do job acima.
Full job CRUD, plus control of the daemon that runs the jobs. The fields accepted by create/update are in the Job fields section above.
SDKs oficiais: Go · Python · JavaScript.
Official SDKs: Go · Python · JavaScript.
Criar um job
Create a job
CronJob é um mapa genérico nos três SDKs, os campos são os mesmos da tabela acima, o SDK não trava um conjunto fixo.
CronJob is a generic map in all three SDKs, the fields are the same as the table above, the SDK doesn't lock in a fixed set.
import (
"context"
"fmt"
alicercelabs "github.com/alicercelabs/alicercesdk-go"
)
client := alicercelabs.New("alk_...")
ctx := context.Background()
job, err := client.Cron.Create(ctx, alicercelabs.CronJob{
"name": "backup-diario",
"schedule": "0 3 * * *",
"image_type": "image",
"image_source": "alpine:latest",
"command": "echo backup",
})
if err != nil {
panic(err)
}
fmt.Println(job["id"])from alicercelabs import AlicerceLabs
client = AlicerceLabs(api_key="alk_...")
job = client.cron.create(
name="backup-diario",
schedule="0 3 * * *",
image_type="image",
image_source="alpine:latest",
command="echo backup",
)
print(job["id"])import { AlicerceLabs } from "alicercesdk-javascript";
const client = new AlicerceLabs("alk_...");
const job = await client.cron.create({
name: "backup-diario",
schedule: "0 3 * * *",
image_type: "image",
image_source: "alpine:latest",
command: "echo backup",
});
console.log(job.id);Listar / buscar um job
List / get a job
jobs, err := client.Cron.List(ctx)
job, err := client.Cron.Get(ctx, job["id"].(string))jobs = client.cron.list()
job = client.cron.get(job["id"])const jobs = await client.cron.list();
const one = await client.cron.get(job.id as string);Atualizar / remover / disparar
Update / delete / trigger
id := job["id"].(string)
_, err = client.Cron.Update(ctx, id, alicercelabs.CronJob{"schedule": "0 4 * * *"})
err = client.Cron.Trigger(ctx, id) // roda agora, fora do agendamento
err = client.Cron.Delete(ctx, id)client.cron.update(job["id"], schedule="0 4 * * *")
client.cron.trigger(job["id"]) # roda agora, fora do agendamento
client.cron.delete(job["id"])const id = job.id as string;
await client.cron.update(id, { schedule: "0 4 * * *" });
await client.cron.trigger(id); // roda agora, fora do agendamento
await client.cron.delete(id);Controlar o daemon
Control the daemon
só uma instância do daemon roda por conta, não por job.
only one daemon instance runs per account, not per job.
status, err := client.Cron.WorkerStatus(ctx)
err = client.Cron.WorkerStart(ctx)
err = client.Cron.WorkerStop(ctx)status = client.cron.worker_status()
client.cron.worker_start()
client.cron.worker_stop()const status = await client.cron.workerStatus();
await client.cron.workerStart();
await client.cron.workerStop();Limites
Limits
1.000/dia · 41/hora por operação: create, list, get, update, delete, trigger e os três endpoints de worker/ contam separado. Um script que só lista jobs não come a cota de quem está criando.
Por container: 0.5 CPU / 256m de memória por padrão (configurável por job, ver "Campos do job" acima), execução morta em 3600s por padrão, no máximo 86400s (24h). Sem limite documentado de quantos jobs um cliente pode ter: o que existe é a cota de create acima, que rege a taxa de criação, não o total.
1,000/day · 41/hour per operation: create, list, get, update, delete, trigger and the three worker/ endpoints are counted separately. A script that only lists jobs doesn't eat into the quota of whoever is creating them.
Per container: 0.5 CPU / 256m memory by default (configurable per job, see "Job fields" above), execution killed at 3600s by default, capped at 86400s (24h). No documented limit on how many jobs a client can have: what exists is the create quota above, which governs the creation rate, not the total.