> For the complete documentation index, see [llms.txt](https://run.emailchaser.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://run.emailchaser.com/api-reference/workspace.md).

# Workspace

## Get the workspace billing profile

> Returns the registrant and postal contact details held for the workspace. These are the details filed with the registrar when the Done For You flow buys a domain, and the address a CAN-SPAM footer must carry. Returns 404 until a profile has been set.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.BillingProfileResult":{"type":"object","properties":{"billingProfile":{"$ref":"#/components/schemas/models.BillingProfile"}}},"models.BillingProfile":{"type":"object","properties":{"addressLineOne":{"type":"string"},"addressLineTwo":{"type":"string"},"city":{"type":"string"},"company":{"type":"string"},"country":{"description":"Country is an ISO 3166-1 alpha-2 code, uppercase.","type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string"},"phoneCc":{"description":"PhoneCc is the telephone country calling code without the plus.","type":"string"},"postalAddress":{"description":"PostalAddress is the address rendered on one line, ready to paste into a\ncompliance footer. Read-only; it is derived from the fields above.","type":"string"},"postalCode":{"type":"string"},"state":{"type":"string"},"updatedAt":{"description":"UpdatedAt is when the profile was last written (RFC3339).","type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorBillingProfileNotFound":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/space/billing-profile":{"get":{"description":"Returns the registrant and postal contact details held for the workspace. These are the details filed with the registrar when the Done For You flow buys a domain, and the address a CAN-SPAM footer must carry. Returns 404 until a profile has been set.","tags":["Workspace"],"summary":"Get the workspace billing profile","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"200":{"description":"The workspace billing profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.BillingProfileResult"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"404":{"description":"No billing profile set for this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorBillingProfileNotFound"}}}},"500":{"description":"Failed to retrieve the billing profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## Set the workspace billing profile

> Creates or replaces the workspace's registrant and postal contact details. Idempotent: sending the same body twice leaves the same state. Every field except addressLineTwo is required, because incomplete registrant details are filed with the registrar just as readily as complete ones. Set this before ordering domains through /dfy/orders.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.BillingProfileResult":{"type":"object","properties":{"billingProfile":{"$ref":"#/components/schemas/models.BillingProfile"}}},"models.BillingProfile":{"type":"object","properties":{"addressLineOne":{"type":"string"},"addressLineTwo":{"type":"string"},"city":{"type":"string"},"company":{"type":"string"},"country":{"description":"Country is an ISO 3166-1 alpha-2 code, uppercase.","type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string"},"phoneCc":{"description":"PhoneCc is the telephone country calling code without the plus.","type":"string"},"postalAddress":{"description":"PostalAddress is the address rendered on one line, ready to paste into a\ncompliance footer. Read-only; it is derived from the fields above.","type":"string"},"postalCode":{"type":"string"},"state":{"type":"string"},"updatedAt":{"description":"UpdatedAt is when the profile was last written (RFC3339).","type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.UpdateBillingProfileRequest":{"type":"object","required":["addressLineOne","city","company","country","firstName","lastName","phone","phoneCc","postalCode","state"],"properties":{"addressLineOne":{"type":"string"},"addressLineTwo":{"type":"string"},"city":{"type":"string"},"company":{"type":"string"},"country":{"description":"Country is an ISO 3166-1 alpha-2 code, e.g. US. Case-insensitive on input.","type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string"},"phoneCc":{"description":"PhoneCc is the telephone country calling code without the plus, e.g. 1.","type":"string"},"postalCode":{"type":"string"},"state":{"type":"string"}}}}},"paths":{"/space/billing-profile":{"put":{"description":"Creates or replaces the workspace's registrant and postal contact details. Idempotent: sending the same body twice leaves the same state. Every field except addressLineTwo is required, because incomplete registrant details are filed with the registrar just as readily as complete ones. Set this before ordering domains through /dfy/orders.","tags":["Workspace"],"summary":"Set the workspace billing profile","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"200":{"description":"The stored billing profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.BillingProfileResult"}}}},"400":{"description":"Invalid billing profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"500":{"description":"Failed to save the billing profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.UpdateBillingProfileRequest"}}},"description":"Registrant and postal details","required":true}}}}}
```

## Get workspace details and members

> Returns the workspace behind the API key: campaign counts broken down by status, and the list of members with their roles. Useful for confirming which workspace a key belongs to.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.SpaceDetails":{"type":"object","properties":{"completed_campaigns":{"type":"integer"},"draft_campaigns":{"type":"integer"},"members":{"type":"array","items":{"$ref":"#/components/schemas/models.MemberInfo"}},"not_started_campaigns":{"type":"integer"},"paused_campaigns":{"type":"integer"},"running_campaigns":{"type":"integer"},"space_id":{"type":"integer"},"total_campaigns":{"type":"integer"},"total_members":{"type":"integer"}}},"models.MemberInfo":{"type":"object","properties":{"created_at":{"type":"string"},"email_address":{"type":"string"},"name":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/space/members":{"get":{"description":"Returns the workspace behind the API key: campaign counts broken down by status, and the list of members with their roles. Useful for confirming which workspace a key belongs to.","tags":["Workspace"],"summary":"Get workspace details and members","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"200":{"description":"Workspace details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.SpaceDetails"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"500":{"description":"Failed to retrieve workspace details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## List workspaces

> Lists the workspaces owned by the API key's workspace owner: the main workspace and its sub-workspaces. isCurrent marks the workspace the calling key is bound to.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.ListWorkspacesResponse":{"type":"object","properties":{"total":{"type":"integer"},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/models.WorkspaceItem"}}}},"models.WorkspaceItem":{"type":"object","properties":{"createdAt":{"type":"string"},"iconUrl":{"description":"IconURL is where the workspace's icon image can be loaded from. Empty\nwhen the workspace has none. Icons are uploaded in the app.","type":"string"},"id":{"type":"integer"},"isCurrent":{"description":"IsCurrent is true for the workspace the calling API key is bound to.","type":"boolean"},"name":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/workspaces":{"get":{"description":"Lists the workspaces owned by the API key's workspace owner: the main workspace and its sub-workspaces. isCurrent marks the workspace the calling key is bound to.","tags":["Workspace"],"summary":"List workspaces","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"200":{"description":"Workspaces owned by the key's workspace owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ListWorkspacesResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"500":{"description":"Failed to list workspaces","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}}}}}}
```

## Create a workspace

> Creates a sub-workspace under the API key's main workspace and, by default, mints a read+write API key bound to the new workspace. The key's fullKey is returned exactly once and cannot be retrieved again, and it never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. Must be called with a main workspace's key: keys bound to sub-workspaces are refused. API keys are included in every plan; workspaces are a Professional-plan feature.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.CreateWorkspaceResponse":{"type":"object","properties":{"apiKey":{"description":"ApiKey is null when generateApiKey was false.","allOf":[{"$ref":"#/components/schemas/models.CreatedApiKey"}]},"note":{"description":"Note reminds integrators that fullKey is not retrievable later.","type":"string"},"workspace":{"$ref":"#/components/schemas/models.WorkspaceItem"}}},"models.CreatedApiKey":{"type":"object","properties":{"expiresAt":{"description":"ExpiresAt is set when the key expires: it inherits the calling key's\nown expiry, so a temporary key never mints a permanent one. Null for\nkeys minted by a non-expiring key.","type":"string"},"fullKey":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}},"models.WorkspaceItem":{"type":"object","properties":{"createdAt":{"type":"string"},"iconUrl":{"description":"IconURL is where the workspace's icon image can be loaded from. Empty\nwhen the workspace has none. Icons are uploaded in the app.","type":"string"},"id":{"type":"integer"},"isCurrent":{"description":"IsCurrent is true for the workspace the calling API key is bound to.","type":"boolean"},"name":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.CreateWorkspaceRequest":{"type":"object","required":["name"],"properties":{"generateApiKey":{"description":"GenerateApiKey mints a read+write API key bound to the new workspace.\nDefaults to true; pass false to create the workspace only.","type":"boolean"},"name":{"description":"Name of the new workspace. Trimmed; 1-60 characters.","type":"string"}}}}},"paths":{"/workspaces":{"post":{"description":"Creates a sub-workspace under the API key's main workspace and, by default, mints a read+write API key bound to the new workspace. The key's fullKey is returned exactly once and cannot be retrieved again, and it never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. Must be called with a main workspace's key: keys bound to sub-workspaces are refused. API keys are included in every plan; workspaces are a Professional-plan feature.","tags":["Workspace"],"summary":"Create a workspace","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true}],"responses":{"201":{"description":"The created workspace and its API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateWorkspaceResponse"}}}},"400":{"description":"Invalid request body or calling key is not bound to a main workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"403":{"description":"The workspace's plan does not include workspaces, or the subscription is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"500":{"description":"Failed to create workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateWorkspaceRequest"}}},"description":"Workspace to create","required":true}}}}}
```

## Create an API key for a workspace

> Mints an API key bound to a workspace the calling key already owns: its own workspace, or one of its sub-workspaces when called with a main workspace's key. The key returned by POST /r/workspaces is shown once and cannot be retrieved again, so this is how a workspace whose key was lost gets a new one without a UI step. The new key authorizes the named workspace only and never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. fullKey is returned exactly once.

```json
{"openapi":"3.1.1","info":{"title":"Emailchaser API","version":"1.0"},"servers":[{"url":"https://api.emailchaser.com/r"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"models.CreateWorkspaceApiKeyResponse":{"type":"object","properties":{"apiKey":{"$ref":"#/components/schemas/models.CreatedApiKey"},"note":{"description":"Note reminds integrators that fullKey is not retrievable later.","type":"string"},"workspace":{"$ref":"#/components/schemas/models.WorkspaceItem"}}},"models.CreatedApiKey":{"type":"object","properties":{"expiresAt":{"description":"ExpiresAt is set when the key expires: it inherits the calling key's\nown expiry, so a temporary key never mints a permanent one. Null for\nkeys minted by a non-expiring key.","type":"string"},"fullKey":{"type":"string"},"id":{"type":"integer"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}},"models.WorkspaceItem":{"type":"object","properties":{"createdAt":{"type":"string"},"iconUrl":{"description":"IconURL is where the workspace's icon image can be loaded from. Empty\nwhen the workspace has none. Icons are uploaded in the app.","type":"string"},"id":{"type":"integer"},"isCurrent":{"description":"IsCurrent is true for the workspace the calling API key is bound to.","type":"boolean"},"name":{"type":"string"}}},"models.ErrorResponse":{"type":"object","properties":{"error":{"type":"string"}}},"models.ErrorUnauthorized":{"type":"object","properties":{"error":{"type":"string"}}},"models.CreateWorkspaceApiKeyRequest":{"type":"object","properties":{"name":{"description":"Name shown in the app's API key list. Defaults to\n\"<workspace name> key\". Trimmed; up to 60 characters.","type":"string"},"readOnly":{"description":"ReadOnly mints a key that can call GET routes (plus the free\nPOST /r/audience/size) and nothing else. Defaults to false, which mints\na read+write key.","type":"boolean"}}}}},"paths":{"/workspaces/{id}/api-keys":{"post":{"description":"Mints an API key bound to a workspace the calling key already owns: its own workspace, or one of its sub-workspaces when called with a main workspace's key. The key returned by POST /r/workspaces is shown once and cannot be retrieved again, so this is how a workspace whose key was lost gets a new one without a UI step. The new key authorizes the named workspace only and never outlives the caller: when the calling key has an expiry, the minted key carries the same expiresAt. fullKey is returned exactly once.","tags":["Workspace"],"summary":"Create an API key for a workspace","parameters":[{"schema":{"type":"string"},"description":"Bearer <API_KEY>","name":"Authorization","in":"header","required":true},{"schema":{"type":"integer"},"description":"Workspace ID","name":"id","in":"path","required":true}],"responses":{"201":{"description":"The workspace and its new API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateWorkspaceApiKeyResponse"}}}},"400":{"description":"Invalid workspace ID or request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorUnauthorized"}}}},"403":{"description":"The workspace's plan does not include the API, or the subscription is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"404":{"description":"No such workspace under this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}},"500":{"description":"Failed to create API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.ErrorResponse"}}}}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/models.CreateWorkspaceApiKeyRequest"}}},"description":"Key options"}}}}}
```
