Rework import_tickets.py from the retired JSON `latest.json` model to the new hourly full-snapshot CSV export. Strictly INC (CRQ out of scope). - Ingest the newest automations/inc/<EAT-timestamp>.csv; skip-if-unchanged by comparing S3 ETag to tickets.import_meta.metadata.source_etag. - Upsert on ticket_id (PK; no dups, never delete -> closure history accrues). No truncate. On success, move processed files to automations/inc/processed/. - Clean at ingest: drop is_alarm=true + the "EXPORT STOPPED..." sentinel; drop week_*, source_s3_*/source_snapshot_id, department/source_type; lowercase region, uppercase raw_status; keep service_type + bucket. - Force path-style S3 addressing; --inc-csv for local dev; --from-bucket for cron. - Add migrations/02 (import_meta + freshness); refresh README/.env.example/docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.7 KiB
n8n S3 Export Workflows
Overview
Both workflows run in the Africa/Nairobi timezone and use the
Rahamafresh Tickets S3 credential.
| Workflow | n8n ID | Source | Bucket |
|---|---|---|---|
| Fuel Records S3 Export | IP2KNAfFazAjTesh |
logistics_department.fuel_records |
fuel |
| FTTH Automation Ticket S3 Export | JI3QkcJeHk9eYRsY |
isp_department_crq.automations and isp_department_osp.automations |
tickets |
Schedules
- Hourly delta export:
10minutes after each hour, from01:10through23:10(0 10 1-23 * * *). - Daily full export:
00:05(0 5 0 * * *). - The
00:05run exports rows up to the end of the previous local day. For example, Wednesday's run exports the snapshot through Tuesday23:59:59. - The full export has its own state and does not read or advance the hourly delta pointer.
Fuel Records S3 Export
Source and change tracking
The source is logistics_department.fuel_records. The table has an indexed
updated_at column and an update trigger that refreshes it whenever a row is
changed.
Hourly runs select rows where:
updated_at > last_successful_delta_export_at
AND updated_at <= requested_at
The delta pointer advances to the maximum exported updated_at only after all
S3 uploads complete and the downloaded latest.json passes validation. A
failed or empty run does not move the pointer incorrectly.
The full run selects all rows created before local midnight, independently of the delta pointer.
Fuel object keys
Every successful run updates:
fuel_records/latest.jsonfuel_records/latest.csv
Delta runs with changed rows also write:
fuel_records/changes/YYYY-MM-DDTHH-mm-ss.jsonfuel_records/changes/YYYY-MM-DDTHH-mm-ss.csv
Full runs write:
fuel_records/full/YYYY-MM-DD.jsonfuel_records/full/YYYY-MM-DD.csv
Exports larger than 5,000 rows additionally produce numbered JSON and CSV
parts such as -part-0001.
Fuel state
Fuel state is stored in n8n workflow static data and is updated only after S3 read-back validation:
last_successful_delta_export_atlast_successful_full_export_daterows_exporteddestination_keyanddestination_keysn8n_execution_idsuccesserror_messagecompleted_at
FTTH Automation Ticket S3 Export
Source and change tracking
The workflow requests an export package from the active SCOREBOARD service:
POST /api/v1/ftth/automation-export/package
Datasets:
- CRQ:
isp_department_crq.automations - INC:
isp_department_osp.automations
Hourly delta packages select records changed after the last successful delta export. The SCOREBOARD service creates an export run before returning the package. n8n uploads every returned object and then calls:
POST /api/v1/ftth/automation-export/mark-sent
Only a SUCCESS acknowledgement advances the delta pointer. Upload failure
marks the run FAILED and preserves the previous pointer.
Full packages use the previous local date as snapshot_date, select the
complete current-state dataset through the previous day, and update only the
full-export date after successful upload.
FTTH object keys
CRQ:
automations/crq/latest.jsonautomations/crq/latest.csvautomations/crq/changes/YYYY-MM-DDTHH-mm-ss.jsonautomations/crq/changes/YYYY-MM-DDTHH-mm-ss.csvautomations/crq/full/YYYY-MM-DD.jsonautomations/crq/full/YYYY-MM-DD.csv
INC:
automations/inc/latest.jsonautomations/inc/latest.csvautomations/inc/changes/YYYY-MM-DDTHH-mm-ss.jsonautomations/inc/changes/YYYY-MM-DDTHH-mm-ss.csvautomations/inc/full/YYYY-MM-DD.jsonautomations/inc/full/YYYY-MM-DD.csv
Exports larger than 5,000 rows additionally produce numbered JSON and CSV parts.
FTTH state
State and audit history are stored in
ftth_automation.automation_export_runs. Each run records:
- export type and requested timestamp
- last successful delta timestamp
- last successful full export date
- snapshot date
- row count
- destination object keys
- n8n execution ID
- status (
PENDING,SUCCESS, orFAILED) - completion timestamp and error summary
File Contents
JSON files contain:
{
"metadata": {
"exported_at": "...",
"export_type": "delta or full",
"source_schema": "...",
"source_table": "...",
"dataset": "crq, inc, or omitted for fuel",
"row_count": 0,
"last_successful_delta_export_at": "...",
"last_successful_full_export_date": "...",
"snapshot_date": "...",
"n8n_execution_id": "..."
},
"records": []
}
Fuel metadata names the previous and candidate delta pointers explicitly. CSV files contain the same exported records as tabular rows with a header line. CSV files do not contain the JSON metadata envelope.
Manual Tests
Both workflows expose production POST webhooks.
Fuel:
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"export_type":"delta"}' \
https://n8n.firesideafrica.cloud/webhook/fuel-records-s3-export
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"export_type":"full"}' \
https://n8n.firesideafrica.cloud/webhook/fuel-records-s3-export
FTTH:
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"export_type":"delta"}' \
https://n8n.firesideafrica.cloud/webhook/ftth-automation-s3-export
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"export_type":"full","force":true}' \
https://n8n.firesideafrica.cloud/webhook/ftth-automation-s3-export
After a test:
- Confirm the webhook response has
success: true. - Open the execution ID in n8n and confirm every upload succeeded.
- Confirm the response lists the expected bucket and destination keys.
- Check the S3 object timestamps and inspect the JSON metadata and row count.
- For FTTH, confirm the matching export run is
SUCCESS.
Production test record
Tests run on June 15, 2026:
| Workflow | Type | Execution | Result |
|---|---|---|---|
| Fuel Records S3 Export | Delta | 402524 |
Success; 0 changed rows; latest JSON and CSV validated |
| Fuel Records S3 Export | Full | 402527 |
Success; 1,965 rows; snapshot date 2026-06-14 |
| FTTH Automation Ticket S3 Export | Delta | 402530 |
Success; CRQ and INC latest/change JSON and CSV written |
| FTTH Automation Ticket S3 Export | Full | 402536 |
Success; 44,114 rows; snapshot date 2026-06-14; 28 objects including batch parts |
Troubleshooting
- Check the n8n execution and identify whether the source query/package, upload, read-back validation, or mark-sent step failed.
- Confirm the
Rahamafresh Tickets S3credential can write to the configured bucket. - For fuel, inspect workflow static data. Do not manually advance
last_successful_delta_export_atafter a failed run. - Verify
fuel_records.updated_atis populated and its update trigger exists if fuel changes are missing. - For FTTH, inspect
ftth_automation.automation_export_runs, includingstatus,destination_object_keys,n8n_execution_id, anderror_summary. - Confirm the SCOREBOARD health endpoint is healthy and that the configured export token and base URL are correct.
- Re-run the appropriate manual webhook after fixing the failure. A failed run leaves the last successful pointer unchanged, so the rows are retried.
Published Version Check
In n8n, open each workflow and confirm:
Activeis enabled.- The saved
versionIdequalsactiveVersionId. - The trigger list contains the hourly schedule, daily
00:05schedule, and manual webhook. - A new production webhook execution uses the same active version and returns the expected destination keys.
Current published versions as of June 15, 2026:
- Fuel Records S3 Export:
6833e5e5-97a0-41be-8f82-9ec612de92ce - FTTH Automation Ticket S3 Export:
b2171088-eac2-439b-97e8-83dfa8117783