Organizations and projects
How your organization, team roles and projects fit together, and how a request decides which project it acts in.
Your organization owns everything billable and shared: credits, members, API keys and webhooks. Inside it, projects keep work apart. Every generation job, asset and product run belongs to exactly one project, and every organization starts with a project called Default.
# Generate into a specific project by slug
curl -X POST https://api.windpaint.ai/v1/generation/capabilities/image.generate \
-H "Authorization: Bearer $WINDPAINT_API_KEY" \
-H "X-Windpaint-Project: campaign-q4" \
-H "Content-Type: application/json" \
-d '{"prompt": "a ceramic teapot on a linen tablecloth, soft window light"}'
Organizations
An organization is created for you when you sign up (email and password with email confirmation, Google, or GitHub). Windpaint is in early access, so a new account waits on the waitlist until it’s approved; approval grants 200 free credits that expire after 30 days. See Billing.
Each user belongs to one organization. The organization holds:
- The credit balance. Credits are org-wide; every project spends from the same balance.
- Members and their roles.
- API keys and account webhooks.
- Projects, and through them every job, asset and run.
Members and roles
Each member has one role. The four roles are fixed; you can’t define your own.
| Role | Summary |
|---|---|
owner | Everything. The person who signed up is the owner. |
admin | Members, API keys, webhooks, organization settings, projects, generation and billing. |
member | Generates and reads. Can see projects, billing and their own API keys, but can’t create keys or change settings. |
read_only | Reads everything a member can, plus webhooks. Can’t generate. |
What each role can do, by permission:
| Action | Permission | owner | admin | member | read_only |
|---|---|---|---|---|---|
| Submit, cancel and upload (generation, products) | generation:write | ✓ | ✓ | ✓ | |
| View jobs, assets, runs, models | generation:read | ✓ | ✓ | ✓ | ✓ |
| View projects | projects:read | ✓ | ✓ | ✓ | ✓ |
| Create, rename and archive projects | projects:write | ✓ | ✓ | ||
| View balance, statement, usage | billing:read | ✓ | ✓ | ✓ | ✓ |
| Buy credits, set the low-balance threshold | billing:write | ✓ | ✓ | ||
| List your own API keys | api_keys:read | ✓ | ✓ | ✓ | ✓ |
| Create and edit API keys | api_keys:create | ✓ | ✓ | ||
| Revoke API keys | api_keys:revoke | ✓ | ✓ | ||
| View webhooks | webhooks:read | ✓ | ✓ | ✓ | |
| Create, edit and delete webhooks | webhooks:create / update / delete | ✓ | ✓ | ||
| View members | users:read | ✓ | ✓ | ✓ | ✓ |
| Invite, change roles, remove members | users:create / update / delete | ✓ | ✓ | ||
| Edit organization settings | orgs:update | ✓ | ✓ |
A request without the permission it needs gets a 403. See Authentication for the response body.
You can’t hand out more access than you hold. An admin can’t promote someone to owner, and can’t create an API key bound to the owner role.
Inviting your team
Invites are managed in the dashboard.
Open Settings → Team
In the dashboard, go to Settings → Team and choose Invite. You need the owner or admin role.
Pick the email and role
Enter the person’s email and pick admin, member or read_only. Pick the smallest role that does the job; you can change it later.
They accept
Windpaint emails an invitation link that is valid for 14 days. If it expires or gets lost, resend it from the same page; resending invalidates the previous link.
From Settings → Team you can also change a member’s role or remove them. Removing a member deactivates their account, so their API keys stop working on the next request. Rotate any automation that depended on them before you remove them.
Membership changes fire account webhook events: member.invited, member.joined, member.role_changed and member.removed.
Projects
A project is a container for jobs, assets and runs. Use one per app, environment, client or campaign; nothing is shared between projects except the credit balance.
- Default project. Every organization has one, named “Default” with the slug
default. It’s used whenever a request doesn’t name a project. It can’t be archived. - Slugs. Each project has a slug that you can use anywhere a project id is accepted. Slugs are 1 to 64 characters of lowercase letters, digits and hyphens, and can’t start or end with a hyphen. If you don’t pass one, it’s derived from the name (
"Campaign Q4"becomescampaign-q4). Slugs are unique within your organization; a taken slug returns409. - Archiving. Archiving a project makes it read-only. Its jobs and assets stay readable, but any write into it (submitting a job, uploading, starting a product run) returns
409with"The project is archived.". There’s no unarchive today. - Assets across projects. A job’s inputs must be assets in the same project.
GET /v1/generation/assets/{id}reads an asset from any project in your organization.
Creating a project
{
"data": {
"id": "5b0f6c1e-8d2a-4f3b-9e71-0c4d2a9b7e15",
"organization_id": "c3a1e8f2-6b4d-4e0a-8f1c-2d7b9e5a1c33",
"name": "Campaign Q4",
"slug": "campaign-q4",
"description": "Holiday product shots",
"is_default": false,
"is_archived": false,
"created_by": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
"created_at": "2026-10-04T14:12:09.481220Z",
"updated_at": null,
"archived_at": null
}
}
You can also create, rename and archive projects in the dashboard under Settings → Projects. The full endpoint list is in the Projects API reference.
Which project a request uses
Every request that reads or writes jobs, assets or runs resolves one project, in this order:
project_idin the request body (generation submits, estimates and product runs). Accepts an id or a slug.- The
X-Windpaint-Projectheader. Accepts an id or a slug. - Your organization’s default project.
The first one present wins; they aren’t merged. A project that doesn’t exist in your organization returns 404. List endpoints such as GET /v1/generation/requests and GET /v1/generation/assets only return items from the resolved project, so send the header on reads too.
GET /v1/generation/requests/{id}/status and GET /v1/generation/assets/{id} find the item anywhere in your organization, so polling a job doesn’t need the header.
The dashboard’s project switcher sends X-Windpaint-Project for you.
Selecting a project in the CLI
The CLI saves a default project in its config file and sends it as X-Windpaint-Project on every command:
windpaint projects # list; * marks the selected one
windpaint projects select campaign-q4 # save by slug or id
windpaint projects select --clear # go back to the default project
A --project flag or the WINDPAINT_PROJECT environment variable overrides the saved project for one command or one shell. See CLI configuration.