Skip to main content

Send predictions to Amazon S3

An Amazon S3 destination writes every prediction served to your account into a bucket you own, as gzipped JSON Lines. Your AWS account grants access to a role, and that role trusts Constellation's OpenID Connect (OIDC) issuer for your account only. No access keys change hands.

Destinations receive predictions served through the API and through agents over MCP. Playground runs are not sent. Deliveries do not count toward your plan's predictions.

How the trust works​

  1. Constellation signs a short-lived token for each delivery. Its issuer is Constellation's OIDC issuer, its audience is sts.amazonaws.com, and its subject is your account ID (acct_...).
  2. Constellation calls AWS STS AssumeRoleWithWebIdentity with that token and your role's ARN.
  3. AWS checks the token's signature against the issuer's published keys and checks that the subject is yours. Only then does it return temporary credentials for the role.
  4. Constellation writes the object with those temporary credentials.

A token for any other Constellation account carries a different subject, so your role refuses it.

What the stack creates​

The CloudFormation stack, constellation-destination, creates:

ResourcePurpose
IAM OIDC identity providerRegisters Constellation's issuer with audience sts.amazonaws.com.
IAM roleTrusts that provider only when the token's subject is your account ID.
Role policyAllows s3:PutObject on arn:aws:s3:::<bucket>/<prefix>/* and nothing else.

Its RoleArn output is the value you paste into the Platform. The stack does not create or change the bucket.

Connect​

  1. Open Destinations in the Platform. On the Amazon S3 card, enter the bucket, a prefix (default constellation), and the bucket's region.
  2. Choose Launch setup in AWS. CloudFormation opens in that region with the template and parameters filled in. Acknowledge that it creates IAM resources and create the stack.
  3. When the stack shows CREATE_COMPLETE, copy the RoleArn output, paste it into Role ARN, and choose Connect Amazon S3.
  4. The destination is Pending while Constellation writes a check object, then Active, or Error with the reason AWS gave.

Use Test on a connected destination to write another check object. You can connect up to three destinations per account.

Object layout​

s3://<bucket>/<prefix>/model=<family>/date=YYYY-MM-DD/hour=HH/<object>.jsonl.gz

family is snr or demand. Dates and hours are UTC. Each line is one prediction, with the fields of an item from GET /predictions:

{"link_id": "link-a", "horizon_minutes": 1, "predicted_at": "2026-10-06T12:00:05Z", "model_version": "snr-baseline-v1", "feature_view_id": "snr-features-v1", "model_release_id": "snr-release-example", "value": {"snr_db_p10": 8.99, "snr_db_p50": 9.33, "snr_db_p90": 9.66}}

Query with Athena​

Partition projection lets Athena find new hours without a crawler. Replace the bucket and prefix:

CREATE EXTERNAL TABLE constellation_predictions (
link_id string,
horizon_minutes int,
predicted_at string,
model_version string,
feature_view_id string,
model_release_id string,
value map<string, double>
)
PARTITIONED BY (model string, `date` string, `hour` string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://my-fleet-data/constellation/'
TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.model.type' = 'enum',
'projection.model.values' = 'snr,demand',
'projection.date.type' = 'date',
'projection.date.format' = 'yyyy-MM-dd',
'projection.date.range' = '2026-01-01,NOW',
'projection.hour.type' = 'integer',
'projection.hour.range' = '0,23',
'projection.hour.digits' = '2',
'storage.location.template' = 's3://my-fleet-data/constellation/model=${model}/date=${date}/hour=${hour}/'
);

-- Weakest links one minute ahead today
SELECT link_id, min(value['snr_db_p50']) AS snr_p50_db
FROM constellation_predictions
WHERE model = 'snr' AND "date" = cast(current_date AS varchar) AND horizon_minutes = 1
GROUP BY link_id
ORDER BY snr_p50_db
LIMIT 10;

Demand rows carry offered_bytes_p10, offered_bytes_p50, and offered_bytes_p99 in value.

Revoke​

Delete the constellation-destination stack. The role disappears, so the next delivery fails and the destination shows Error. Then choose Remove on the destination to stop attempts. Objects already written stay in your bucket.

Troubleshooting​

SymptomCause and fix
Error mentioning AssumeRoleWithWebIdentity or AccessDenied on the roleThe ARN is not the stack's RoleArn output, or the stack was created for another Constellation account. Launch setup again from this account's Destinations page.
Error mentioning AccessDenied on PutObjectThe bucket or prefix differs from the stack's parameters, or a bucket policy denies the role. Encrypted buckets using a customer managed KMS key also need the key policy to allow the role kms:GenerateDataKey.
Error mentioning NoSuchBucket or a regionThe bucket name or region is wrong. Remove the destination and connect it again with the right values.
Active, but no new objectsDestinations send only predictions that are served. Request forecasts through the API or an agent.
Not available in this environmentDestinations are switched off in this environment. Read forecasts from the API instead.