- Docs
- Guides
- Advanced
- Storage destinations
- Upload to Amazon S3
Upload to Amazon S3
Send every rendered image or PDF to an Amazon S3 bucket you control.
HTML/CSS to Image connects to Amazon S3 by assuming an IAM role in your AWS account. HCTI receives temporary credentials when it needs to write an object, so you do not need to create or share a long-lived AWS access key.
Before you begin
Section titled “Before you begin”You need:
- An existing S3 bucket and its AWS region.
- Permission to create an IAM role and attach a permissions policy.
- An optional key prefix, such as
hcti-renders, if HCTI objects should stay within one part of the bucket.
Open the Storage Destinations dashboard, click Add storage destination, and choose Amazon S3. Enter the bucket, region, and optional key prefix first; HCTI uses those values to generate scoped example policies.
Create the IAM role
Section titled “Create the IAM role”The dashboard generates a trust policy and a role permissions policy. They answer different questions and belong in different parts of the IAM role.
Add the trust policy
Section titled “Add the trust policy”The trust policy controls who can assume the role. It contains:
- The HTML/CSS to Image storage writer role as the principal.
sts:AssumeRoleas the allowed action.- An
sts:ExternalIdcondition unique to your HCTI organization.
In AWS IAM, go to Roles, choose Create role, select Custom trust policy, and paste the dashboard-generated trust policy. The external ID prevents the HCTI writer role from using this role for a different organization. Use the generated value without modifying or reusing it.
The trust policy only allows HCTI to assume the role. It does not grant access to your S3 bucket.
Attach the permissions policy
Section titled “Attach the permissions policy”Attach the dashboard-generated role permissions policy as an inline or managed policy. It scopes access to the bucket and optional prefix entered in HCTI.
| Permission | Required? | Purpose |
|---|---|---|
s3:PutObject |
Required | Writes rendered images and the connection-test object. |
s3:GetObject |
Optional | Reloads an original for a later resize, crop, or format conversion when Disable HCTI Storage is selected. |
s3:DeleteObject |
Optional | Removes the small object created by Test connection. It is not used to delete rendered images. |
The generated resources cover rendered objects under arn:aws:s3:::<bucket>/<key-prefix>/*, including connection-test objects under <key-prefix>/.hcti/connection-tests/.
Write access is enough to connect and upload base renders. Include read access if HCTI may need to create a transformation after the initial render while HCTI Storage is disabled. Include delete access if you want connection tests to clean up after themselves.
Finish the destination in HCTI
Section titled “Finish the destination in HCTI”- Finish creating the role and copy its ARN.
- Return to the storage destination form and paste the ARN into IAM role ARN.
- Confirm the bucket, AWS region, and optional key prefix.
- Click Test connection.
- Enable and save the destination after the test succeeds.
- Copy the destination
id.
The connection test assumes the role and writes a small text object. Cleanup is best-effort, so a missing s3:DeleteObject permission does not make a successful write test fail.
Upload an HCTI image to S3
Section titled “Upload an HCTI image to S3”Pass the destination ID as storage_destination_id when you create an image:
curl -X POST https://hcti.io/v1/image \ -u 'user-id:api-key' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com", "storage_destination_id": "your-storage-destination-id" }'Rendering is lazy. With normal HCTI storage enabled, request the image URL returned by the API to render the image and write both the HCTI copy and the S3 copy. With Disable HCTI Storage selected, send an authenticated PUT request to the /v1/store/... URL returned by the create request.
The base object is stored as <key-prefix>/<image-id>.png, or .pdf when the request uses pdf_options. Resized, cropped, and reformatted versions use separate object keys.
Troubleshooting Amazon S3 uploads
Section titled “Troubleshooting Amazon S3 uploads”- HCTI cannot assume the role — Confirm that the trust relationship contains the generated HCTI principal and the exact organization-specific external ID.
- The connection test returns Access Denied — Confirm that the role permissions policy allows
s3:PutObjecton the bucket and configured prefix, and that a bucket policy does not explicitly deny the request. - The connection test uses the wrong endpoint — Confirm that the AWS region in HCTI matches the bucket’s region.
- A later transformation fails with HCTI Storage disabled — Add
s3:GetObjectfor the configured object prefix. - Connection-test objects remain — Add
s3:DeleteObjectfor the.hcti/connection-tests/path.
AWS documents creating a role with a custom trust policy and using external IDs for third-party access.
Automate destination setup
Section titled “Automate destination setup”You can also automate configuration through the Storage destinations API or MCP tools. GET /v1/storage-destinations/aws-external-id and get_aws_storage_external_id return both external_id and writer_role_arn; both operations require storage_destinations:create_update and take no destination ID.
Use writer_role_arn as Principal.AWS and external_id in a StringEquals condition on sts:ExternalId when creating your role’s trust policy. This supplies both values programmatically, without copying the principal from the dashboard. Attach the bucket permissions described above, then pass your role’s ARN as the destination’s connection_info.role_arn. The returned writer_role_arn identifies HCTI’s existing role, which assumes your role; it is not the destination’s role ARN.
Need help?
Talk to a human. Email support@htmlcsstoimage.com and we’ll help you get started.