Skip to content
campaiDeveloper
campai.com

Save a contact-review report that counts missing email and postal addresses. A Schedule group supplies the server main() function and cron configuration; you can run the report manually while developing it. Before using it for a recurring business process, verify that your environment actually dispatches the published schedule.

The reviewed implementation contains schedule configuration, publication, and manual simulation. An automatic recurring dispatcher was not established by those code paths. Do not treat “saved a cron pattern” or “simulation succeeded” as evidence that a timed run will occur.

What you’ll have at the end

A saved contact-review report with checked totals, a preserved previous result on failure, and a separate timed-dispatch check.

  1. Read review queue
  2. Count issues
  3. Save report
  4. Verify timed dispatch
Before you start
  • An unpublished app with Database access
  • Review entries from the record-change group, or an empty queue
  • A recorded time zone for later dispatch verification
On this page

In your app, open Edit → Add group, select Schedule, and enter a name identifying the job. The default schedule controls show Pattern, Hour, and Minute for a daily schedule. Choose the intended pattern and time, then create the group.

Open the Schedule group’s menu → Settings to review Pattern, Hour and Minute. Verify actual dispatch separately.

Open the Schedule group’s menu → Settings to review Pattern, Hour and Minute. Verify actual dispatch separately.

Schedule settings showing a daily pattern at hour 9 and minute 0

Use the group's settings to review or change its schedule. The stored configuration is a cron expression. A schedule without the time-zone semantics you intended can run at the wrong local time, particularly around daylight-saving changes. Verify the actual dispatch time before depending on a daily business deadline.

Do not publish a frequent task while you are still debugging code that writes records or calls a paid service. Start with a harmless function and an unpublished app.

Enable Settings → Database access and put this complete program in the group's server index.ts. Use the same app as React to record changes to report on its review list:

Example

export async function main() {
    const report = { checkedAt: new Date().toISOString(), contacts: 0, missingEmail: 0, missingAddress: 0 }
    const entries = storage.list<{ issues: string[] }>({ prefix: ['contact-review'] }, { limit: 501, batchSize: 100 })

    for await (const entry of entries) {
        report.contacts += 1
        if (report.contacts > 500) throw new Error('More than 500 review items: add paginated reporting before replacing the report')
        if (entry.value.issues.includes('Add an email address')) report.missingEmail += 1
        if (entry.value.issues.includes('Add a postal address')) report.missingAddress += 1
    }

    await storage.set(['contact-review-report'], report)
    console.log(report)
}

It takes no input. Save any changes, choose the group's Simulate action, wait for validation and build, then choose Start simulation. Confirm the logged totals and the successful execution result. The job saves the same object under ['contact-review-report'] for another app group to read.

An empty queue produces zero counts. With one contact missing both details, expect contacts: 1, missingEmail: 1, and missingAddress: 1. Run it again: it replaces one report rather than appending another. An incomplete read leaves the previous report intact, so consumers should display checkedAt. If the final save times out, read back the stored report before deciding whether it was replaced.

storage.list yields entries through an async iterator and fetches additional pages as needed. The 501st item deliberately fails this small example instead of publishing a misleading total of 500. For larger queues, implement a resumable report using the iterator's cursor and a checkpoint. Increasing the limit alone may exceed the runtime timeout.

The job reports on the app's review queue, not the entire organization. It needs Database access, but no organization API permissions or outbound host.

The report replaces the saved summary only after all review entries have been counted.

The report replaces the saved summary only after all review entries have been counted.

Review report source in the Schedule group

Use a disposable test app containing only a harmless schedule group for this check:

  1. Set a near-future execution time and record the expected time zone and UTC instant.
  2. Publish the app version containing that schedule.
  3. Do not press Simulate during the observation window; that would create a manual result you could mistake for a timed run.
  4. Check Logs and the group's last-run information after the scheduled time.
  5. Confirm a second scheduled run before treating recurrence as working.

If no automatic run appears, keep the job out of production and confirm scheduler support for the environment. Changing the function body will not repair a missing dispatch mechanism. A server process you control can instead run an external API integration under your own scheduler; use Make your first API request for that execution model.

Once automatic dispatch is verified, design each run around a persisted work identifier or time window. Read pending items, claim or mark the work in a way that prevents concurrent processing, process a bounded batch, and record the result. A repeated run should recognize completed work rather than repeat its effects.

Do not keep the only checkpoint in a module-level variable: a later invocation can run in a different execution context. Use an explicitly enabled app database or a suitable external store and verify its behavior. Avoid assumptions of exactly-once execution or catch-up after downtime unless your environment proves those guarantees.

Set a timeout that fits the job and keep work bounded. A timeout ends your wait or execution; it cannot undo an external service action that already happened. Review server access and secrets before introducing those calls.

Track successful completion separately from a run starting. Log a job identifier, batch size, and completion outcome without logging personal data or credentials. Treat an absent expected run as an operational failure rather than waiting for an application exception.

After editing source or schedule settings, publish the intended update and verify the next timed run again. For release and rollback behavior, see Publish and share an app.

Check your result

Empty queue
A saved report with zero counts.
Repeated run
Replaces the same report rather than appending duplicates.
Oversized or failed read
Leaves the last complete report intact.
Continue with Debug with logs and file history