Amazon S3 delivery
JSON2Video can upload every rendered video to a bucket in your own Amazon S3 account. You create an IAM user that may only upload to your bucket, save its access key once in the dashboard, and reference that saved destination from your movies. Each finished video then lands in your bucket, with no download and re-upload on your side.
Only Amazon S3 is supported for now. S3-compatible services that use their own endpoint (Cloudflare R2, Wasabi, DigitalOcean Spaces, Backblaze B2, MinIO) are not.
Set it up
You need access to the AWS console with permission to create IAM users.
1. Pick a bucket and note its region
Use an existing bucket or create one in the S3 console. The default settings of a new bucket are fine: keep Block all public access on. JSON2Video does not need a public bucket.
Write down:
- the bucket's name, for example
my-videosβ only the name: nos3://, no URL, no folder; - its region, for example
eu-west-1. You find it in the bucket list (AWS Region column) or in the bucket β Properties β AWS Region.
Choose a folder for the videos too, for example videos/. In S3 a folder is just the start of the file's name, so there is nothing to create.
2. Create an IAM user
In the IAM console, open Users β Create user. Name it, for example json2video-uploader. It does not need access to the AWS console, and it needs no permissions yet: you add them in the next step.
Create a user for JSON2Video only β not your own user, and not one shared with other tools. Its key is stored by JSON2Video, so it should be able to do nothing but upload.
3. Attach the minimal policy
Open the user β Permissions β Add permissions β Create inline policy β JSON, and paste:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:AbortMultipartUpload"],
"Resource": "arn:aws:s3:::BUCKET/FOLDER/*"
}]
}
Replace BUCKET with the bucket name and FOLDER with your folder, without a slash at the end: arn:aws:s3:::my-videos/videos/*. To allow the whole bucket, leave the folder out: arn:aws:s3:::my-videos/*. Give the policy a name (for example json2video-upload) and create it.
- Scope it to the folder before any macro. If your folder uses macros, put in the policy only the part before the first macro. For the folder
videos/__yyyy__/__mm__/, usearn:aws:s3:::my-videos/videos/*. A policy onvideos/2026/*would let this year's videos through, but the test would fail, and deliveries would stop in January. s3:PutObjectwrites the videos.s3:AbortMultipartUploadlets JSON2Video cancel an upload that cannot finish, so no unfinished parts stay in your bucket (videos larger than 8 MB are uploaded in parts).- JSON2Video never lists, reads or deletes anything in your bucket, so the policy needs no
s3:ListBucket,s3:GetObjectors3:DeleteObject.
The dashboard shows this policy, filled in with the bucket and folder you type, under Minimal IAM policy, with a copy button.
If the bucket is encrypted with an AWS KMS key that you manage, the user also needs permission to use that key: see Encrypted buckets (SSE-KMS).
4. Create an access key
Open the user β Security credentials β Access keys β Create access key. As the use case, choose Third-party service (or Application running outside AWS), confirm, and create the key.
AWS shows two values:
- the Access key ID: 20 characters, starting with
AKIA; - the Secret access key: 40 characters. AWS shows it only once. Copy it now or download the
.csvfile. If you lose it, create a new access key.
Use a long-term access key of an IAM user (it starts with AKIA). Temporary keys (they start with ASIA, for example from AWS IAM Identity Center, an assumed role or aws sts) are refused: they stop working within hours.
5. Add the destination in the dashboard
In Dashboard β Connections β Output destinations, click Add destination β Amazon S3 and fill in:
| Field | What to enter |
|---|---|
| Connection ID | The name you use in your movies, for example my-s3. See Connection IDs. |
| Bucket | The bucket name, for example my-videos. |
| Region | The bucket's region, for example eu-west-1. |
| Access key ID | The key's ID (AKIAβ¦). |
| Secret access key | The secret access key. It is stored encrypted and never shown again. When you edit the destination, leave it empty to keep the stored one. |
| Folder | Optional. The folder for the videos, for example videos/__yyyy__/. Empty means the root of the bucket. |
| File name | Optional. Empty means the rendered file's own name (__filename__). |
6. Test it
Click Test destination in the form, or Test in the row menu once it is saved. JSON2Video writes a small text file, json2video-test.txt, to your folder and shows the result. See The test file.
7. Use it in a movie
Reference the destination by its ID in exports[].destinations. Copy JSON in the row menu copies this snippet:
{
"resolution": "full-hd",
"scenes": [ /* ... */ ],
"exports": [{
"destinations": [
{ "type": "aws-s3", "id": "my-s3" }
]
}]
}
A movie may change the folder and the file name for one render:
{
"exports": [{
"destinations": [{
"type": "aws-s3",
"id": "my-s3",
"remote-path": "customers/acme/__yyyy__/",
"file": "acme-promo-__random__.mp4"
}]
}]
}
Nothing else can be set in the movie. The bucket, the region and the keys always come from the saved destination:
- Never put AWS keys in the movie JSON.
POST /v2/moviesrefuses a movie whose destinations carry AWS keys (access-key-id,secret-access-keyor a similar field, or an access key ID anywhere in anaws-s3destination): HTTP400, codeaws_keys_in_movie. Nothing is rendered. If those keys were real, rotate them in AWS: they were sent in a request. - Any other field next to
id(for examplebucketorregion) makes the delivery fail withIn an aws-s3 destination the movie can only set remote-path and file. β¦. Nothing is uploaded. - There is no inline form: an
aws-s3destination withoutidfails withAn aws-s3 destination needs the ID of a saved connection β¦. "type": "aws-s3"is optional next toid. If you write it, it must match the saved destination's type.
Properties
| Property | Dashboard field | Required | Can the movie set it? | Notes |
|---|---|---|---|---|
type |
β | no | yes | "aws-s3". Optional next to id; must match the saved type. |
id |
Connection ID | yes | yes | The saved destination. |
bucket |
Bucket | yes | no | Bucket name only: 3β63 characters, lowercase letters, numbers, dots and hyphens. JSON2Video's own buckets are refused. |
region |
Region | yes | no | For example eu-west-1. China (cn-β¦) and GovCloud (us-gov-β¦) regions are not supported. |
access-key-id |
Access key ID | yes | no | 20 characters, starting with AKIA. |
secret-access-key |
Secret access key | yes | no | 40 characters. Stored encrypted, never shown again. |
remote-path |
Folder | no | yes | The folder. Empty means the root of the bucket. |
file |
File name | no | yes | Defaults to __filename__, the rendered file's own name (for example abc123.mp4). |
The object's name in S3 (its key) is the folder and the file name joined with /. A leading / or ./ and double slashes are ignored, so /videos//2026/ becomes videos/2026/. A .. folder is refused, and the whole key can be at most 1,024 bytes.
File names and folders
remote-path and file accept the same macros as FTP and SFTP: __yyyy__, __mm__, __dd__, __hh__, __nn__, __ss__, __random__, __filename__ and more. See Filename macros.
{
"remote-path": "customers/acme/__yyyy__/__mm__/",
"file": "acme-promo-__yyyy__-__mm__-__dd__-__random__.mp4"
}
Keep the .mp4 extension in file: it sets the file's content type (see below).
How the upload works
- Only the video is uploaded, not its thumbnail.
- Existing files are overwritten. A key that already exists is replaced, without an error. On a bucket with versioning, S3 keeps the previous version. Use
__random__or the date and time macros for unique names. - Private by default. JSON2Video sends no ACL, no storage class and no encryption setting: the file gets your bucket's own settings (default encryption, Object Ownership, Block Public Access). It stays private unless your bucket makes it public.
- Content type:
video/mp4when the file name ends in.mp4, otherwiseapplication/octet-stream. - Large videos are streamed to your bucket in parts of 8 MB.
- Failed renders: there is no video, so Amazon S3 destinations are skipped (
Nothing to upload: the render did not produce a video.). - Wrong region: if the bucket is not in the region you saved, AWS tells JSON2Video the right one and the video is still delivered, but the result adds a warning, for example
The bucket is in eu-west-1, not us-east-1: edit the connection.Correct the region to remove it. - Temporary errors from AWS or the network are retried automatically within the same delivery. The result counts it as 1 attempt.
- Timeout: all destinations of a render share a budget of about 5 minutes. A very large video, or slow destinations before it in the list, can use it up. The upload is then stopped and cancelled (
Stopped: the delivery ran out of time.). - Order and failures: destinations run one after another, in the order of the array. A failed upload does not stop the destinations after it, and the movie's
statusstaysdone. The result is recorded in the movie'sdestinations_result, under Render logs β the render β Deliveries, and in the Last delivery column of the saved destination.
The test file
Test checks the bucket, the region, the access key and the policy with one real upload:
- It writes
json2video-test.txtto the part of the Folder before its first macro. For the foldervideos/__yyyy__/, that isvideos/json2video-test.txt. With an empty folder, or one that starts with a macro, the file goes to the root of the bucket. - The file holds one line:
JSON2Video wrote this file to test the destination "my-s3" on <date and time>. You can delete it. - JSON2Video never deletes it β the minimal policy does not allow deleting. Delete it yourself, or leave it: the next test overwrites it without an error.
- It never lists, reads or deletes anything in your bucket.
- It uses the values in the form, saved or not. An empty Secret access key field uses the stored one.
- It waits up to 10 seconds for AWS.
- It counts towards the limit of 20 tests per hour.
When it works, the result reads like Wrote json2video-test.txt to bucket my-videos, folder videos (the part of the remote path before its first macro). JSON2Video does not delete it: you can. Otherwise it shows one of the messages in Troubleshooting.
If the region you chose is not the bucket's, the test still works but its result is shown as a warning, Destination works, but the region is wrong, ending with The bucket is in eu-west-1, not us-east-1: edit the connection. In the form, click Use eu-west-1 to switch the Region field to the bucket's region, then save.
The test fails, but deliveries work? The test writes to the folder before the first macro, which is above the folders your deliveries use. A policy narrower than that folder β for example videos/2026/* for the folder videos/__yyyy__/ β lets deliveries through and denies the test. Scope the policy to the part before the first macro (videos/*), as the dashboard's Minimal IAM policy does.
Recommended bucket settings
Delete incomplete multipart uploads
Videos larger than 8 MB are uploaded in parts. When an upload is stopped (for example because the time budget ran out), JSON2Video cancels it. If the cancellation does not reach AWS, the parts already uploaded stay in the bucket: you don't see them in the file list, but AWS bills them as storage. A lifecycle rule removes them:
- In the S3 console, open the bucket β Management β Create lifecycle rule.
- Name it, for example
delete-incomplete-uploads, and apply it to all objects in the bucket. - Under Lifecycle rule actions, tick Delete expired object delete markers or incomplete multipart uploads.
- Tick Delete incomplete multipart uploads and set Number of days to
1. - Create the rule.
Encrypted buckets (SSE-KMS)
Buckets encrypted with Amazon S3 managed keys (SSE-S3, the default) need nothing more. If the bucket's default encryption uses an AWS KMS key that you manage, the IAM user also needs kms:GenerateDataKey and kms:Decrypt on that key (kms:Decrypt is needed for uploads in parts). Add a second statement to the user's policy:
{
"Effect": "Allow",
"Action": ["kms:GenerateDataKey", "kms:Decrypt"],
"Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID"
}
The key's own policy must not deny the user either. Without these permissions, deliveries fail with Bucket my-videos encrypts objects with an AWS KMS key this access key may not use. β¦.
Why AWS denies the upload (AccessDenied)
The access key may not write to bucket β¦ means AWS denied the upload. The IAM user's policy is the usual cause, but not the only one:
- the policy allows another folder than the one the video goes to β check the
remote-pathof the destination and of your movies; - a bucket policy with a
Denythat matches the upload, for example one that only allows some IP addresses or VPC endpoints (JSON2Video does not upload from a fixed IP address), or one that requires an encryption or ACL header (JSON2Video sends neither); - a service control policy (SCP) or resource control policy of your AWS Organization;
- a permissions boundary on the IAM user.
Troubleshooting
Messages shown by Test and in the delivery results (destinations_result, Deliveries). my-videos, videos, my-s3 and the regions stand for your own values.
| Message | Cause and fix |
|---|---|
AWS does not know this access key ID: it was deleted, deactivated or mistyped. Check it in IAM (the user β Security credentials) and edit the connection. |
The access key ID is wrong, or the key was deleted or deactivated. Edit the destination, or create a new access key. |
The secret access key does not match the access key ID. Paste the secret access key again in the connection. |
Wrong or incomplete secret access key. If you no longer have it, create a new access key. |
The access key may not write to bucket my-videos, folder videos. Allow s3:PutObject on arn:aws:s3:::my-videos/videos/* in the IAM user's policy. |
AWS denied the upload. Check the policy's Resource against the folder, then the other causes. For the root of the bucket it reads The access key may not write to the root of bucket my-videos. Allow s3:PutObject on arn:aws:s3:::my-videos/* in the IAM user's policy. |
Bucket my-videos encrypts objects with an AWS KMS key this access key may not use. Allow kms:GenerateDataKey and kms:Decrypt on that key for the IAM user. |
See Encrypted buckets (SSE-KMS). |
Bucket my-videos does not exist. Check its name in the S3 console and edit the connection. |
Typo in the bucket name, or the bucket was deleted. |
Bucket my-videos is in region eu-west-1, not us-east-1. Edit the connection and choose eu-west-1. |
Wrong region, and AWS refused the upload. Edit the destination. When AWS does not name the right region: Bucket my-videos is not in region us-east-1. Check its region (S3 console β the bucket β Properties β AWS Region) and edit the connection. |
β¦ The bucket is in eu-west-1, not us-east-1: edit the connection. at the end of a success message |
The video was delivered (or the test file written), but the saved region is wrong. Edit the destination. |
Amazon S3 is not available in region <region>. Check the bucket's region (S3 console β the bucket β Properties β AWS Region) and edit the connection. |
The saved region does not exist, or S3 is not available there. |
The connection to Amazon S3 (region eu-west-1) failed: <reason>. |
A network error or timeout between JSON2Video and AWS. Test again in a moment. |
No answer from Amazon S3 for bucket my-videos (region eu-west-1) within 10 s. Try again in a moment. |
Test only: AWS did not answer in time. The test file may or may not have been written. |
Amazon S3 answered <code> (HTTP <status>): <reason>. |
Any other error from AWS, with its code and text. |
Stopped: the delivery ran out of time. / Not attempted: the delivery ran out of time. |
The destinations of the render used up their shared budget of about 5 minutes. List the S3 destination before slower ones. |
The upload to Amazon S3 was stopped before it finished. |
The upload was cancelled before it completed. Render again. |
An aws-s3 destination needs the ID of a saved connection (Dashboard β Connections β Output destinations). |
The movie has an aws-s3 destination without id. Save the destination in the dashboard and reference its ID. |
In an aws-s3 destination the movie can only set remote-path and file. Bucket, region and keys come from the connection. |
Remove the other fields from the movie's destination. |
Connection "my-s3" is of type aws-s3, not ftp. Remove "type" from the movie or use another connection. |
The movie's type does not match the saved destination. |
Connection "my-s3" was not found. Create it in Dashboard β Connections, or remove it from the movie. |
No destination has that ID. IDs are case-sensitive. |
The secret-access-key of connection "my-s3" could not be read. Save the connection again in Dashboard β Connections. |
Enter the secret access key again in the dashboard and save. |
The object key is β¦ bytes long; Amazon S3 allows at most 1024. Shorten remote-path or file. / The object key cannot contain a ".." folder. Edit remote-path or file. |
Fix the folder or the file name. Nothing was uploaded. |
Nothing to upload: the render did not produce a video. (skipped) |
The render failed, so there was no video to upload. |
Messages shown when you save a destination (bucket name, region, key format) are listed in the errors reference.