Weekly HubSpot CRM backup with automatic 90-day cleanup

By General Input

Every Sunday night your contacts, companies and deals are exported to a dated folder you control, and copies older than 90 days are cleaned up for you.

Integrations

  • HubSpot
  • General Input Storage
  • Slack Bot

Type

Deterministic Code

Categories

  • Operations
  • Sales

Every Sunday at 2am, back up my HubSpot CRM into General Input Storage so I have a durable point in time copy that I control. Use a cron trigger.

Put the settings at the very top of the workflow as named constants so they are obvious and easy to change: RETENTION_DAYS = 90, OBJECT_TYPES = ["contacts", "companies", "deals"], BACKUP_PREFIX = "hubspot-backup", and SLACK_CHANNEL for the channel that gets the summary. Everything downstream reads from these, so dropping "deals" from OBJECT_TYPES or moving retention to 30 days should be a one line edit and nothing else.

For each object type in the list, page through the entire object set using HubSpot's List Contacts, List Companies, and List Deals operations. Request 100 records per page, which is the maximum for the CRM v3 list endpoints, and follow the cursor at paging.next.after until it is absent. Do not use the search endpoints for this: they cap at 10,000 total results, and a backup has to include everything. Ask for an explicit property list per object type rather than the default handful, so the export captures the fields people actually rely on.

Write each object type to its own CSV. Put the record id in the first column, followed by one column per property. Union the property keys across every record of that type first, so a record that is missing a property still lines up with the header row. Quote any field containing a comma, a quote, or a newline, since CRM notes and addresses routinely contain all three.

Upload each CSV to General Input Storage using Presign Upload, under a timestamped key like hubspot-backup/2026-08-30/contacts.csv built from the run date. Keys are store relative paths with forward slashes and no leading slash, and there are no real folders, so the date segment is what creates the grouping. Pass the true byte length as sizeBytes, since that is what the quota check uses, then PUT the bytes to the returned URL with the headers it returns and Content-Type text/csv. Presigned URLs are valid for 15 minutes, so mint one per file at the moment you upload that file, and never store or reuse one.

Only after every upload in this run has succeeded, prune old backups. Call List Objects with the BACKUP_PREFIX prefix, paging through the cursor, and parse the date segment out of each returned key. Group keys by that date, and for any date older than RETENTION_DAYS, remove every object under it with Delete Object. Delete Object is idempotent, so a key that is already gone is not a failure. If any upload failed, skip the prune entirely and say so in the Slack message: deleting the only good copies right after a failed backup is the one outcome that must never happen.

Finish by posting to the channel in SLACK_CHANNEL with the Slack Bot Send a Message operation. Include the row count for each CSV, the size of each file and the total size of the snapshot, the full prefix the snapshot was written to, and which old backup dates were removed along with how many objects that covered. If nothing was old enough to prune, say that explicitly rather than staying silent, so a quiet week still looks different from a broken workflow.

For reliability: if a HubSpot page fails, retry it a few times with a backoff, and respect the Retry-After header on a 429 rather than hammering the API. If an object type cannot be exported completely, do not upload a partial CSV under a name that looks complete. Report that type as failed in the Slack message and leave the existing backups untouched.

Related prompts

Explore more prompts
A brand asset library your marketing team actually searchesTurn Mailjet email clicks into ranked HubSpot follow-upsClean out the Looker dashboards and Looks nobody opensLiveKit live operations console for room moderationWake up dormant Keap leads with a researched reasoniMessage campaign console with pre-flight checks and delivery boardLiveChat coverage board for planning next week's shiftsPhone routing control panel for LiveKit voice agentsA searchable RFP answer library your bid team drafts fromLinkedIn Ads budget pacing dashboard for every client account