Skip to content

S3 Bulk Data Export

Steer publishes a daily snapshot of your tenant’s data to a dedicated Amazon S3 bucket. The payload schema matches the data model — the same columns, the same semantics — delivered as gzipped JSON files you can pull on your own schedule.

1️⃣ Receive Credentials

Steer provisions a dedicated IAM user for your tenant and emails you the AWS access key and secret key.

2️⃣ List Your Prefix

Every object lives under s3://s3-steer-dwh-prod-export/{tenant_id}/. List it to discover what’s available.

3️⃣ Download & Decompress

Files are gzip-compressed JSON. Download, gunzip, and parse with any standard tooling.

Steer will send your AWS credentials to the email address on file during onboarding.

🔐 What You Receive

  • AWS_ACCESS_KEY_ID — unique per tenant
  • AWS_SECRET_ACCESS_KEY — store in a secret manager, never commit to source control
  • Your tenant_id — the UUID that prefixes all of your objects (e.g. fb296814-1cd0-4417-b3ea-d76092b82841)

The IAM user tied to your credentials is scoped to read-only access on your tenant’s prefix only. The attached policy grants:

s3:ListBucket
s3:GetObject
s3:GetObjectVersion
s3:GetBucketLocation

No write, delete, or cross-tenant access is possible with these keys.

All objects follow a predictable path structure:

s3://s3-steer-dwh-prod-export/
└── {tenant_id}/
├── appointment/
│ └── appointment_2026-04-16.gzip
├── campaign/
│ ├── campaign_2026-04-15.gzip
│ └── campaign_2026-04-16.gzip
├── customer/
│ └── customer_2026-04-16.gzip
├── invoice/
│ └── invoice_2026-04-16.gzip
├── message_directmail/
├── message_email/
├── message_text/
├── review/
├── shop/
├── survey/
└── vehicle/
  • Object key pattern: {tenant_id}/{data_type}/{data_type}_{YYYY-MM-DD}.gzip
  • Case sensitivity: tenant_id values are case-sensitive in S3 keys — preserve the exact casing of the UUID you receive.
  • Cadence: one file per data type, per day
  • Contents: a full snapshot of the tenant’s data for that type as of the export time — not a delta
  • Format: gzip-compressed JSON, schema-identical to the corresponding Analytics API endpoint
  • Retention: objects are retained indefinitely; historical dates remain downloadable
FolderSchema matchPrimary keyUpdated-at column
appointment/Appointment data modelSTEER_APPOINTMENT_IDSTEER_UPDATED_AT
campaign/Campaign data modelSTEER_CAMPAIGN_IDSTEER_UPDATED_AT
customer/Customer data modelSTEER_CUSTOMER_IDSTEER_UPDATED_AT
invoice/Invoice data modelSTEER_INVOICE_IDSTEER_UPDATED_AT
message_directmail/Message (direct mail) data modelSTEER_MESSAGE_IDSTEER_UPDATED_AT
message_email/Message (email) data modelSTEER_MESSAGE_IDSTEER_UPDATED_AT
message_text/Message (text) data modelSTEER_MESSAGE_IDSTEER_UPDATED_AT
review/Review data modelSTEER_REVIEW_IDSTEER_UPDATED_AT
shop/Shop data modelSTEER_SHOP_IDSHOP_UPDATED_AT
survey/Survey data modelSTEER_SURVEY_IDSTEER_UPDATED_AT
vehicle/Vehicle data modelSTEER_VEHICLE_IDSTEER_UPDATED_AT

Field-level definitions live on the individual Data Models pages in the sidebar (Customer, Vehicle, Invoice, Appointment, Campaign, Message, Review, Survey, and Shop).

Replace the placeholder values with your credentials and tenant_id.

Terminal window
AWS_ACCESS_KEY_ID=YOUR_KEY \
AWS_SECRET_ACCESS_KEY=YOUR_SECRET \
aws s3 ls s3://s3-steer-dwh-prod-export/YOUR_TENANT_ID/ --recursive

Example output:

2026-04-15 14:23:51 201516 YOUR_TENANT_ID/campaign/campaign_2026-04-15.gzip
2026-04-16 11:53:43 201109 YOUR_TENANT_ID/campaign/campaign_2026-04-16.gzip
2026-04-15 14:24:04 47138770 YOUR_TENANT_ID/customer/customer_2026-04-15.gzip
2026-04-16 11:53:58 47174332 YOUR_TENANT_ID/customer/customer_2026-04-16.gzip
2026-04-15 14:26:57 563377780 YOUR_TENANT_ID/invoice/invoice_2026-04-15.gzip
2026-04-16 11:56:54 563957725 YOUR_TENANT_ID/invoice/invoice_2026-04-16.gzip
...

Because each file is a full snapshot, clients compute deltas themselves. The recommended approach is to download two consecutive days and join on the data type’s primary key.

Strategy 1 — Filter by STEER_UPDATED_AT (fastest)

Section titled “Strategy 1 — Filter by STEER_UPDATED_AT (fastest)”

Every record carries an update timestamp. To find what changed on 2026-04-16, you only need the later snapshot:

import gzip, json
from datetime import datetime, timezone
with gzip.open("invoice_2026-04-16.gzip") as f:
rows = json.load(f)
cutoff = datetime(2026, 4, 15, tzinfo=timezone.utc)
changed = [r for r in rows if datetime.fromisoformat(r["STEER_UPDATED_AT"]) > cutoff]
print(f"{len(changed):,} invoices changed since {cutoff.date()}")

This catches inserts and updates but not deletes.

Strategy 2 — Diff two snapshots on the primary key (complete)

Section titled “Strategy 2 — Diff two snapshots on the primary key (complete)”

When you need to detect deletes too, load both days and diff on the primary key:

import gzip, json
def load(path, pk):
with gzip.open(path) as f:
return {r[pk]: r for r in json.load(f)}
PK = "STEER_INVOICE_ID"
prev = load("invoice_2026-04-15.gzip", PK)
curr = load("invoice_2026-04-16.gzip", PK)
inserted = curr.keys() - prev.keys()
deleted = prev.keys() - curr.keys()
updated = {
k for k in curr.keys() & prev.keys()
if curr[k]["STEER_UPDATED_AT"] != prev[k]["STEER_UPDATED_AT"]
}
print(f"+{len(inserted)} ~{len(updated)} -{len(deleted)}")

See the primary-key table above for the correct PK column per data type.

  • Timezone: the date in the filename reflects the UTC date of export.
  • Publish time: files typically land by ~12:00 UTC. If a given day’s file is missing, re-check later or fall back to the previous day’s snapshot — no partial files are published.
  • No notifications: Steer does not currently emit S3 event notifications. Poll the prefix or schedule downloads against expected publish time.
  • Versioning: object versions are preserved (s3:GetObjectVersion is granted), but in normal operation a given {data_type}_{date}.gzip key is written once.
  • Missed-day recovery: if a daily file is missing past the publish window, contact Steer support — files can be regenerated within the retention window.
SymptomLikely causeFix
AccessDenied on aws s3 lsWrong tenant_id in the path, or listing the bucket rootList exactly s3://s3-steer-dwh-prod-export/{your_tenant_id}/ (case-sensitive)
NoSuchKey for today’s fileExport hasn’t landed yet (before ~12:00 UTC)Retry after the publish window, or use yesterday’s snapshot
InvalidAccessKeyId / SignatureDoesNotMatchCredentials mistyped or rotatedRe-check env vars; request rotation via Steer support if needed
Python MemoryError parsing invoice_*.gzipLoading the full file into memoryUse gzip.GzipFile + streaming parser (ijson)

Need help? Contact Steer support with your tenant_id and the S3 key you were trying to access.