One S3 bucket, many webQsee teams
Keep one S3 bucket for your whole organisation and run multiple webQsee teams inside it using prefix isolation. Each team gets its own folder root, its own IAM policy and its own group of IAM users.
Estimated time: 30–45 min · Last reviewed: 2026-05 · Difficulty: Intermediate–Advanced
Before you start
- webQsee installed in Chrome or Edge, get it here
- Each team's owner has an active Professional or Team plan, see pricing
- At least two webQsee teams set up, if you haven't yet, see Create and manage multiple webQsee teams
- Familiarity with the multi-user S3 setup, this tutorial builds on that pattern
- An AWS account with rights to create policies, groups, users and buckets
The pattern
Use a single bucket (e.g. webqsee-org) and partition it by a top-level prefix per team:
webqsee-org/
team-a/
shared/
protected/
private/<userId>/
team-b/
shared/
protected/
private/<userId>/
team-c/
...webQsee already supports a prefix field in the Cloud Gallery's Connection settings JSON. When set, all paths the extension reads/writes are transparently prepended with that prefix.
Each team gets its own IAM policy that's identical in shape to the webqsee-user / webqsee-admin policies, but scoped to that team's prefix. Members of team A literally cannot list, read or write objects under team-b/ at the AWS level.
This page walks through setting up team A; repeat for team B, C, and so on.
1. Create the shared bucket (once)
Create the bucket
S3 → Create bucket. Name:
webqsee-org(or whatever fits your naming convention). Region: e.g.eu-central-1. Block all public access: ON. Versioning: Disabled. Encryption: SSE-S3.Sanity check: Green toast "Successfully created bucket". The bucket appears in S3's bucket list.Apply CORS once for the whole bucket
Permissions tab → CORS → Edit, paste:
[ { "AllowedOrigins": [ "https://view.webqsee.com", "https://app.webqsee.com", "chrome-extension://*" ], "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"], "AllowedHeaders": ["*"], "ExposeHeaders": ["ETag", "x-amz-request-id", "x-amz-version-id"], "MaxAgeSeconds": 3000 } ]Save changes.
Sanity check: CORS is saved (green toast). All teams will share this one CORS config, you don't need to redo it per team.
2. Create the per-team policies (repeat per team)
The example below is for "team A", whose prefix is team-a/. For team B, find-and-replace team-a with team-b everywhere and rename to webqsee-team-b-user / webqsee-team-b-admin.
Create policy
webqsee-team-a-adminIAM → Policies → Create policy → JSON tab. Paste:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "TeamAAdminListBucket", "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketCors" ], "Resource": "arn:aws:s3:::webqsee-org", "Condition": { "StringLike": { "s3:prefix": [ "team-a/shared/*", "team-a/shared/", "team-a/protected/*", "team-a/protected/", "team-a/private/*", "team-a/private/", "team-a/", "team-a" ] } } }, { "Sid": "TeamAAdminFullObjects", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetObjectTagging", "s3:PutObject", "s3:PutObjectTagging", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts" ], "Resource": [ "arn:aws:s3:::webqsee-org/team-a/shared/*", "arn:aws:s3:::webqsee-org/team-a/protected/*", "arn:aws:s3:::webqsee-org/team-a/private/*" ] } ] }Save as
webqsee-team-a-admin.Sanity check: Policywebqsee-team-a-adminappears in Customer managed policies.Create policy
webqsee-team-a-userCreate another policy. Paste:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "TeamAUserListBucket", "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketCors" ], "Resource": "arn:aws:s3:::webqsee-org", "Condition": { "StringLike": { "s3:prefix": [ "team-a/shared/*", "team-a/shared/", "team-a/protected/*", "team-a/protected/", "team-a/private/${aws:PrincipalTag/webqsee-user-id}/*", "team-a/private/${aws:PrincipalTag/webqsee-user-id}/", "team-a/", "team-a" ] } } }, { "Sid": "TeamAUserSharedRW", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetObjectTagging", "s3:PutObject", "s3:PutObjectTagging", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts" ], "Resource": "arn:aws:s3:::webqsee-org/team-a/shared/*" }, { "Sid": "TeamAUserPrivateRW", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetObjectTagging", "s3:PutObject", "s3:PutObjectTagging", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts" ], "Resource": "arn:aws:s3:::webqsee-org/team-a/private/${aws:PrincipalTag/webqsee-user-id}/*" }, { "Sid": "TeamAUserProtectedReadOnly", "Effect": "Allow", "Action": ["s3:GetObject", "s3:GetObjectTagging"], "Resource": "arn:aws:s3:::webqsee-org/team-a/protected/*" } ] }Save as
webqsee-team-a-user.Sanity check: Two team-A policies are now in the Customer managed list. The user policy uses the tag condition for per-member/private/isolation.
3. Create the per-team groups
Create group
webqsee-team-a-adminIAM → User groups → Create group. Attach
webqsee-team-a-adminpolicy. Save.Sanity check: Group appears with 1 attached policy.Create group
webqsee-team-a-userSame drill, attach the user policy.
Sanity check: Both team-A groups exist with one policy each.
4. Create IAM users for team A
Look up each member's webQsee user ID
In webQsee → switch to team A using the team-switcher button in the toolbar → Settings → Current User and Team. The roster shows User IDs for that team's members.
Sanity check: You have the (name, webQsee user ID) list for everyone in team A.Create one IAM user per team-A member
IAM → Users → Create user. Username pattern:
<first-name>-team-a. Programmatic only. Group:webqsee-team-a-user(or-admin). Tag: keywebqsee-user-id, value = their webQsee user ID for team A. Generate access key, save securely.Sanity check: Each team-A member has an IAM user in the right group with the matching tag. Access keys are in your password manager.
5. Configure Cloud Gallery for team A
Switch webQsee to team A
In the webQsee toolbar use the team-switcher to make team A the active team. The Cloud Gallery settings you'll edit only apply to that team.
Sanity check: The top of the main window shows "Team: Team A" (or whatever you named it).Set team-A team-wide cloud settings
Gallery → Cloud Gallery → Edit Settings. Fill in:
- Storage Type:
S3 compatible storage, KEY-Auth - Authentication mode:
Per-user credentials (multi-user) - Bucket name:
webqsee-org - Connection settings:
The trailing slash matters, webQsee concatenates it with internal paths.{ "region": "eu-central-1", "prefix": "team-a/" }
Save team settings.
Sanity check: Toast "Team cloud settings saved". The Cloud Gallery panel reads "Cloud storage requires personal credentials", expected because per-user mode is on.- Storage Type:
Enter your personal credentials
Paste your team-A IAM access key + secret. Click Save personal credentials.
Sanity check: "Cloud Storage connected ✓" in green.Ask team-A members to do the same
Each enters their own access key + secret while their active team in webQsee is team A.
Sanity check: Every team-A member reports "Cloud Storage connected ✓".
6. Repeat for team B (and so on)
Repeat steps 2–5 with
team-bSame shape, different prefix. Create
webqsee-team-b-adminandwebqsee-team-b-userpolicies (ARNs usewebqsee-org/team-b/…), matching groups, IAM users tagged with their team-B webQsee user IDs, and set the Connection settings JSON to{ "region": "eu-central-1", "prefix": "team-b/" }.Sanity check: Switching webQsee from team A to team B (toolbar team-switcher) toggles which Cloud Gallery content you see; team-A members see only team-A content, team-B members see only team-B content.
Final sanity check, cross-team isolation
Have an A-only member upload a screenshot to Shared while their active team is A. In the S3 console you should see it appear under team-a/shared/…. Switch a B-only member's webQsee to team B and open Cloud Gallery → Shared, they should not see the team-A object. Even better: ask them to try aws s3 ls s3://webqsee-org/team-a/ from the CLI with their team-B credentials, they should get AccessDenied.
If they can see team-A content from team B, the IAM policy ARNs include the wrong prefix; re-check step 2.
Troubleshooting
- Member sees an empty Shared tab even though uploads worked: the
prefixin the Connection settings JSON is missing or has no trailing slash, so the list call goes toshared/notteam-a/shared/. Re-save with"prefix": "team-a/". - 403 on ListBucket: the policy's
s3:prefixcondition must include the team prefix exactly, e.g."team-a/shared/*". Watch for trailing-slash mismatches. - A user can read team-B content: their IAM user is in the wrong group, or there's an extra inline policy attached directly to the user. Open IAM → User → Permissions and verify only the team-A policy applies.
- "prefix not allowed" warning in webQsee: the prefix string must end with
/and may not start with/. Valid:team-a/. Invalid:/team-a,team-a. - CORS error: CORS is bucket-wide so once applied (step 1) it covers all teams. If you see a CORS error, re-check the bucket Permissions tab.
When to choose the alternative (separate buckets)
Prefix-based isolation is great for organisations where:
- You want one billing line in AWS for all webQsee storage
- Cross-team analytics or backup tooling can target one bucket
- You trust the IAM policy boundary to enforce separation
Choose one bucket per team when:
- Compliance or audit requires hard bucket-level separation
- Per-team data retention or lifecycle rules differ
- You want to be able to hand a bucket over to a team without slicing it out of a shared one
Related tutorials
Create multiple webQsee teams
Set up several webQsee teams under one organisation and administrate them centrally.
Open tutorialMulti-user S3 with RBAC
The single-team multi-user reference setup this guide is built on top of.
Open tutorialStop guessing. Start webQseeing.
Add webQsee to Chrome or Edge in one click. Most features are free, forever, no signup needed. Upgrade only if your team needs cloud sharing, S3 storage and Pro-grade tooling.
Works in Chrome 103+, Edge 103+ and most Chromium-based browsers. Install instructions.