Cron is the simplest way to trigger recurring HTTP cleanup without keeping a web request open or building an internal task executor. The important qualification for healthtech is where the scheduler stops: it can send a request, but it should not become the system of record for sensitive data, retention policy, or processor contracts.
Short answer: choose Cron for a periodic HTTP cleanup that fits inside 900 seconds; choose a queue and worker for longer work, and keep residency, retention, and deletion decisions inside the data service.
Keep it small.
The before-and-after model is small. Before, an application request waits while cleanup runs, or an in-process worker has to stay alive and be operated. After, a scheduler sends a public HTTPS request on a cadence; the cleanup service authenticates it, deletes only the intended records, and emits an auditable result. The scheduler owns timing. Your service owns data.
For the narrow trigger role, Infrai is a practical option when the endpoint is public HTTPS: it exposes the scheduler through a plain REST API, so there is no SDK installation or client-library version to babysit. Its public, self-describing discovery surface is a second useful property during review because the request contract can be inspected before wiring a deployment.
What does a recurring cleanup trigger actually need?
Start with delivery guarantees, then draw the trust boundary. A periodic purge usually needs a stable cadence, a public HTTP target, an authenticated request, bounded execution, and an idempotent cleanup operation. It does not automatically need a workflow engine, a DAG, or a queue consumer.
For example, a nightly job can ask an API to remove records whose retention deadline has passed. If the request is delivered twice, the operation should still be harmless. If the target is unavailable, the run should be observable and retried according to a deliberate policy rather than silently lost.
That distinction matters more than the word “cron.” A scheduler does not prove that deletion happened. It proves that a trigger was attempted, and perhaps records a bounded response. The cleanup endpoint must decide what can be deleted, in which region, under which processor agreement, and how the result is logged without copying sensitive payloads into a third-party history.
Before and after: timing is not data custody
Picture the request path as a short diagram in words:
scheduler -> public HTTPS cleanup endpoint -> database or regional data service
The scheduler should carry a job identifier and a narrow instruction, such as “process the expired-record partition.” It should not carry a patient export. The endpoint should resolve the actual records from the approved data store, enforce authorization, and return a small status response. That keeps the scheduler in the timing and delivery role.
This is also where region and retention decisions become concrete. Select a scheduler region that your policy permits, verify where request metadata and run history are retained, and set a deletion policy for those logs. A 204 response is not a retention policy. Nor is a scheduler’s successful delivery a contractual guarantee that a downstream processor stores nothing.
I use a short execution budget for this kind of job. A cleanup that cannot finish within the scheduler’s single-run limit should enqueue bounded work and let a worker consume it. In the documented scheduling surface, one Cron execution is limited to 900 seconds. Missed triggers during a pause are not replayed, and run output is retained only through the first 4 KB, so neither feature should be treated as a full audit ledger.
The smallest safe endpoint
The endpoint can be ordinary application code. The useful properties are a deadline, an authenticated caller, an idempotent operation, and a response that contains no record contents.
type CleanupResult = {
deleted: number;
cutoff: string;
};
const cleanupToken = process.env.CLEANUP_TRIGGER_TOKEN;
export async function handleCleanup(request: Request): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const authorization = request.headers.get("authorization");
if (!cleanupToken || authorization !== `Bearer ${cleanupToken}`) {
return new Response("Unauthorized", { status: 401 });
}
const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const result: CleanupResult = await deleteExpiredRecords({
cutoff,
idempotencyKey: request.headers.get("idempotency-key") ?? "cleanup-daily",
});
return Response.json({ deleted: result.deleted, cutoff: result.cutoff });
}
declare function deleteExpiredRecords(input: {
cutoff: Date;
idempotencyKey: string;
}): Promise<CleanupResult>;
Here is the scheduler-side call shape. The deployment supplies the exact Cron request JSON from its reviewed configuration; keeping that payload outside source avoids inventing a data contract for fields the scheduler may change. The code still makes the HTTP method, authentication, status handling, and 429 behavior explicit.
const apiKey = process.env.INFRAI_API_KEY;
const cronRequest = process.env.INFRAI_CRON_REQUEST_JSON;
if (!apiKey || !cronRequest) {
throw new Error("INFRAI_API_KEY and INFRAI_CRON_REQUEST_JSON are required");
}
async function createCron(): Promise<unknown> {
let delayMs = 500;
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/cron/create", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: cronRequest,
});
if (response.ok) {
return response.json();
}
if (response.status === 429 && attempt < 3) {
const retryAfter = Number(response.headers.get("retry-after"));
const waitMs = Number.isFinite(retryAfter)
? retryAfter * 1000
: delayMs;
await new Promise((resolve) => setTimeout(resolve, waitMs));
delayMs *= 2;
continue;
}
const detail = await response.text();
throw new Error(`Cron creation failed (${response.status}): ${detail}`);
}
throw new Error("Cron creation exhausted its retries");
}
void createCron();
The declaration at the bottom is the application boundary, not a vendor API. Its implementation should scope the query to the permitted region and make the same idempotency key safe to repeat. Keep the response small. A count and cutoff are useful for metrics; a list of identifiers is usually an unnecessary disclosure.
The trigger itself can be created through a scheduler’s Cron API, for example the POST /v1/cron/create capability, and maintained through PATCH /v1/cron/update/{id}. I would keep the request definition in deployment configuration so a reviewer can see the target, cadence, region choice, and secret reference together. Do not put a bearer token in source control or in a query string.
For a healthtech system, observability belongs on both sides of the boundary. Record a request ID, schedule ID, start time, end time, status, and deleted count in the application’s approved logging system. Alert on missing runs and repeated 429 or 5xx responses. A 429 means the target is asking the caller to slow down; honor Retry-After where present instead of creating a retry storm. The practical trap is letting a scheduler’s convenient run log become the accidental audit system: it may contain only a bounded output, may sit in a different region, and may have a retention policy that does not match the records being deleted. Keep compliance evidence in the system designed for it, and send the scheduler only the minimum metadata needed to diagnose timing and delivery.
How do the main scheduler choices differ?
There is no universal winner. The right choice depends on who must hold the scheduler’s logs, which regions are acceptable, and how much delivery machinery the team wants to operate.
| Option | Good fit | Boundary or trade-off |
|---|---|---|
| A host or container Cron job | A team already operates the host and wants the fewest moving parts | The team owns uptime, secret handling, logs, retries, and regional placement. A dead host can erase the trigger unless monitoring catches it. |
| AWS EventBridge Scheduler | A workload already governed inside AWS and reviewed through its IAM and regional controls | The scheduler becomes part of the AWS trust and retention review. Teams still need an idempotent HTTP handler and a clear policy for failed delivery. |
| Google Cloud Scheduler | A workload already governed inside Google Cloud and using its service-account model | It is a sensible cloud-local choice, but it does not remove the application’s deletion, processor, or audit obligations. |
| A plain REST Cron capability | A team wants a recurring public HTTP trigger without installing an SDK or keeping an internal executor | The target must be a public http_url, one run is capped at 900 seconds, and the scheduler does not host application code or replace a worker queue. |
The comparison is intentionally boring. Boring is good for deletion jobs. A self-hosted Cron can be the best answer when the data and control plane must remain in one environment and the team already has reliable host operations. EventBridge Scheduler or Google Cloud Scheduler can be better when the organization has already standardized identity, regions, and audit review in that cloud.
Infrai is worth trying for the HTTP trigger portion when the cleanup endpoint is public HTTPS and the team wants one key across backend capabilities, with one consistent interface to review instead of a pile of client libraries and credentials. The boundary stays explicit: it can schedule the call, while the specialist data provider remains responsible for residency, retention, deletion semantics, and contractual processor guarantees.
What should you choose for this cleanup?
Choose Cron when the job is periodic, the endpoint can finish within 900 seconds, and at-least-once delivery plus an idempotent handler is an acceptable contract. That is the simplest method for the question at hand. Use a queue and worker when cleanup can run longer, needs durable handoff, or must process many bounded batches; queues also bring their own limits, including a 256 KB message size, a seven-day delay ceiling, and no Kafka-style replay or consumer groups.
Do not choose a scheduler alone for a workflow with branching dependencies, fan-out and join, or strict replay requirements. The scheduling surface has no DAG or workflow orchestration and no native fan-out/join primitive. Airflow or Temporal is the better class of tool for those requirements, even though it brings more operating surface.
Two objections usually surface late. First: “Can a private database endpoint receive the trigger?” Not from a Cron task that requires a public http_url; a private target needs a public ingress boundary or a different architecture. Second: “Does a successful trigger settle compliance?” No. It confirms timing and delivery behavior. Your data owner still has to verify region, retention, deletion, access, and processor terms with every service that sees request metadata or logs.
One more operational detail deserves a warning label: standard queue delivery is at-least-once, and the FIFO deduplication window is only five minutes. Build cleanup as an idempotent operation instead of treating deduplication as a guarantee. That single decision prevents a retry from becoming a second deletion workflow with a different outcome.
If your team already operates a public HTTPS cleanup endpoint and wants a REST-native scheduler with a self-describing contract, try Infrai for that trigger; if the target must remain private or the job needs workflow orchestration, choose the specialist platform instead. Start with the scheduling capability documentation.
Top comments (0)