Skip to main content
This is the warehouse loop, end to end: run a report, turn its rows into a file, put the file in your bucket. Four steps, one flow, and the file’s contents never travel through it. The last step is the whole integration. GetDialed has no Snowflake connector and needs none — Snowpipe already watches an S3 prefix and ingests what lands there. Put the object in the right prefix and you are done.

Step 1 — Create the IAM user

The credential is yours, and the bucket is yours. Create an IAM user in your own AWS account, scoped to the one bucket you want GetDialed to write into, and hand over its access-key pair. Nothing else in your account is reachable with it.

The IAM policy

Attach this inline to that user, substituting your bucket name. Inline rather than a shared managed policy, so the grant cannot be widened later by a change somebody makes for a different reason.
Two statements, because S3 has two kinds of permission and they take different resource ARNs. The first names the bucket; the second names the objects inside it, which is what the trailing /* is. A policy that puts all five actions on one ARN grants nothing useful, whichever ARN it picks.
s3:ListBucket is on the list even though nothing lists your bucket, and it is not decorative.Without it, S3 answers 403 AccessDenied for an object that simply is not there — instead of 404. That is deliberate on S3’s part: it stops a caller who is not allowed to see inside a bucket from probing it for what it contains, one key at a time.The cost is that a credential lacking s3:ListBucket makes “your object is missing” and “your credential is wrong” the same answer, and nothing on our side can tell them apart to tell you. Granting it on the bucket ARN is what lets a failed fetch or delete be diagnosed at all.It grants no ability to read, write or remove anything — those are the object actions in the second statement.
s3:AbortMultipartUpload is there because a large file is uploaded in parts. If an upload fails part way through, the parts have to be cleaned up — and an upload that cannot abort leaves them behind in your bucket, billed to you, invisible in a normal object listing. Nothing wider is needed. No s3:*, no second bucket, no account-wide "Resource": "*", and no iam or sts statement of any kind. sts:GetCallerIdentity — the call that identifies which AWS account the key belongs to — requires no permissions at all and cannot be denied by policy, so granting it would only imply that it needed granting.

Step 2 — Create the credential

Supply the key pair and the bucket’s region. aws_default_bucket is optional but worth setting: with it, a step that omits bucket uses it, so the bucket name lives in one place rather than in every step.
The create verifies the keys and files the credential under the AWS Account they belong to — you never look up or type the account number. Keep the returned id; it is the connection_id the S3 step selects. Check the grant before you build a flow on it:
Because a default bucket is configured, this proves both halves — that the key pair is valid and that it can actually reach that bucket. Without a default bucket it can only prove the first, and says so in as many words. See credentials for the full method.

Step 3 — Build the flow

Four tasks. The first two are the Five9 report you already run; the last two are new.

What each of the two new steps is doing

create_file writes the set out and hands on a reference. output_mode: "record_set" on the report step already staged the rows as a sealed record set and reported its record_set_id; create_file reads that set as it runs and reports file_id, size_bytes, sha256 and row_count. A report with three million rows costs the flow exactly what a report with three costs it — see files. format: "ndjson" is the shape to pick here. One JSON record per line is what Snowpipe reads a line at a time, so a partially-written or very large file is never a problem for the loader. compress: "gzip" stores the file compressed, and its name gains .gz. That suffix is how Snowflake detects the compression on load — there is nothing to configure at the Snowflake end, and nothing to decompress in between. aws__s3__put streams the file into your bucket. It takes the file_id from the previous step, not its contents. The key is the full path within the bucket and you write it out yourself, deliberately: Snowpipe watches a prefix, so the path is part of the integration rather than a detail for the platform to invent. The key above ends in {{ system.execution_id }}, which is unique to the run — see expressions for the other identifiers available. To partition the prefix by date instead, pass the load date in with the trigger and format it in the key:
An object already at that key is replaced. A key that does not vary per run quietly overwrites yesterday’s load with today’s, and Snowpipe may or may not have ingested it first. Put something that changes — the execution id, a date, a batch id — into the path.

Step 4 — Point Snowpipe at the prefix

Nothing more is needed from GetDialed. On the Snowflake side, create a stage over the same bucket and prefix and a pipe that auto-ingests from it, with a JSON file format for the NDJSON you wrote. Leave the file format’s compression setting on its automatic default, which is what reads the .gz suffix create_file gave the object — that is the whole reason the suffix is worth having. Consult Snowflake’s own documentation for the storage integration, stage, file format and pipe: that half lives entirely in your Snowflake account, and its options change on Snowflake’s schedule rather than ours. The object landing in the bucket is the integration. There is no connector to install and no credential of yours that GetDialed holds for Snowflake.

Confirming a load

Two things are worth recording per run, and both come back from the steps you already have. row_count from create_file reconciles against the report’s own row_count — if they disagree, the file is not what the report said it was. And sha256 is computed over the bytes as stored, so anything that downloads the object can compute the same value and prove it received the file unchanged. To check a file before uploading it — skipping an empty load, for instance — put getdialed__files__get_file_metadata between the two steps and branch on size_bytes.
Cleaning up. The stored file is kept until you delete it. If the copy in your bucket is the one that matters, either add getdialed__files__delete_file as a final step, or set expires_in_days on create_file to 1, 7, 30 or 90 and let it lapse on its own. Deleting the stored file does not touch the object you uploaded.

Next steps

Files

Formats, compression, tags, retention, and all six file steps.

Credentials

The AWS credential method in full, including S3-compatible storage.

Schedule a flow

Make it nightly.

Record transforms

Filter or reshape the report’s rows before writing the file.