1️⃣ Receive Credentials
Steer provisions a dedicated IAM user for your tenant and emails you the AWS access key and secret key.
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 tenantAWS_SECRET_ACCESS_KEY — store in a secret manager, never commit to source controltenant_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:ListBuckets3:GetObjects3:GetObjectVersions3:GetBucketLocationNo 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/{tenant_id}/{data_type}/{data_type}_{YYYY-MM-DD}.gziptenant_id values are case-sensitive in S3 keys — preserve the exact casing of the UUID you receive.| Folder | Schema match | Primary key | Updated-at column |
|---|---|---|---|
appointment/ | Appointment data model | STEER_APPOINTMENT_ID | STEER_UPDATED_AT |
campaign/ | Campaign data model | STEER_CAMPAIGN_ID | STEER_UPDATED_AT |
customer/ | Customer data model | STEER_CUSTOMER_ID | STEER_UPDATED_AT |
invoice/ | Invoice data model | STEER_INVOICE_ID | STEER_UPDATED_AT |
message_directmail/ | Message (direct mail) data model | STEER_MESSAGE_ID | STEER_UPDATED_AT |
message_email/ | Message (email) data model | STEER_MESSAGE_ID | STEER_UPDATED_AT |
message_text/ | Message (text) data model | STEER_MESSAGE_ID | STEER_UPDATED_AT |
review/ | Review data model | STEER_REVIEW_ID | STEER_UPDATED_AT |
shop/ | Shop data model | STEER_SHOP_ID | SHOP_UPDATED_AT |
survey/ | Survey data model | STEER_SURVEY_ID | STEER_UPDATED_AT |
vehicle/ | Vehicle data model | STEER_VEHICLE_ID | STEER_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.
AWS_ACCESS_KEY_ID=YOUR_KEY \AWS_SECRET_ACCESS_KEY=YOUR_SECRET \aws s3 ls s3://s3-steer-dwh-prod-export/YOUR_TENANT_ID/ --recursiveExample output:
2026-04-15 14:23:51 201516 YOUR_TENANT_ID/campaign/campaign_2026-04-15.gzip2026-04-16 11:53:43 201109 YOUR_TENANT_ID/campaign/campaign_2026-04-16.gzip2026-04-15 14:24:04 47138770 YOUR_TENANT_ID/customer/customer_2026-04-15.gzip2026-04-16 11:53:58 47174332 YOUR_TENANT_ID/customer/customer_2026-04-16.gzip2026-04-15 14:26:57 563377780 YOUR_TENANT_ID/invoice/invoice_2026-04-15.gzip2026-04-16 11:56:54 563957725 YOUR_TENANT_ID/invoice/invoice_2026-04-16.gzip...AWS_ACCESS_KEY_ID=YOUR_KEY \AWS_SECRET_ACCESS_KEY=YOUR_SECRET \aws s3 cp \ s3://s3-steer-dwh-prod-export/YOUR_TENANT_ID/invoice/invoice_2026-04-16.gzip \ ./invoice_2026-04-16.gzipDecompress and inspect. Note the non-standard .gzip extension — pass -S .gzip so gunzip accepts the suffix:
gunzip -k -S .gzip invoice_2026-04-16.gziphead -c 500 invoice_2026-04-16Configure credentials via environment variables, IAM role, or ~/.aws/credentials. Do not hardcode access keys.
import boto3import gzipimport json
s3 = boto3.client("s3") # picks up credentials from env / IAM / ~/.aws/credentials
BUCKET = "s3-steer-dwh-prod-export"TENANT = "YOUR_TENANT_ID"KEY = f"{TENANT}/customer/customer_2026-04-16.gzip"
obj = s3.get_object(Bucket=BUCKET, Key=KEY)with gzip.GzipFile(fileobj=obj["Body"]) as gz: records = json.load(gz)
print(f"{len(records):,} customers")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.
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, jsonfrom 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.
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.
s3:GetObjectVersion is granted), but in normal operation a given {data_type}_{date}.gzip key is written once.| Symptom | Likely cause | Fix |
|---|---|---|
AccessDenied on aws s3 ls | Wrong tenant_id in the path, or listing the bucket root | List exactly s3://s3-steer-dwh-prod-export/{your_tenant_id}/ (case-sensitive) |
NoSuchKey for today’s file | Export hasn’t landed yet (before ~12:00 UTC) | Retry after the publish window, or use yesterday’s snapshot |
InvalidAccessKeyId / SignatureDoesNotMatch | Credentials mistyped or rotated | Re-check env vars; request rotation via Steer support if needed |
Python MemoryError parsing invoice_*.gzip | Loading the full file into memory | Use gzip.GzipFile + streaming parser (ijson) |
Need help? Contact Steer support with your tenant_id and the S3 key you were trying to access.