api reference

Organizations API

Create organizations and read user-scoped or organization-scoped inbox metrics.

These routes cover both organization creation and dashboard reporting. Creation is authenticated but not organization-scoped yet. The stats route is user-scoped, while the activity and summary routes require organization scope.

Create organization

POST/api/organizations

Create a new organization for the authenticated user and provision its starter inbox when possible.

Auth
Authenticated endpoint. Organization scope is not required because this route creates the organization.
Success
201

Request headers

Request headers
HeaderExampleRequirementDetails
Cookie session or X-API-KeyCookie: <better-auth session> or X-API-Key: spin_...RequiredAll documented product endpoints require an authenticated user session or a valid Better Auth API key.
Content-Typeapplication/jsonRequiredJSON request body is required.

Request body

Request body
FieldTypeRequirementDetails
namestringRequired

Organization display name.

Constraints: Trimmed length must be between 2 and 64 characters.

Success response

Success response
FieldTypeDetails
organizationobject

Created organization record.

organization.idstring

Organization identifier.

organization.namestring

Organization display name.

organization.slugstring

Resolved unique organization slug.

organization.logostring | null

Organization logo URL when set.

starterAddressIdstring | null

Starter inbox address ID when provisioning succeeds, otherwise null.

seededSampleEmailCountnumber

Number of sample emails seeded into the starter inbox during provisioning.

starterInboxProvisionedboolean

Whether starter inbox setup completed successfully during organization creation.

warningstring | undefined

Present when the organization was created but starter inbox provisioning failed.

Common error responses

Common error responses
StatusErrorWhen it happens
400Organization name must be between 2 and 64 charactersThe request body is missing a valid name or the name is out of range.
400EMAIL_DOMAINS is not configuredThe backend cannot provision the required starter inbox domain.
409Unable to create organization. Please try again.Repeated slug collision retries are exhausted during organization creation.
500Unable to create organizationOrganization creation fails for another backend or auth-layer reason.

Example request

BashRequest to POST /api/organizations
curl -X POST "https://api.spinupmail.com/api/organizations" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: spin_..." \
  -d '{
    "name": "QA Team"
  }'

Example response

JSONResponse payload for POST /api/organizations
{
  "organization": {
    "id": "org_abc123",
    "name": "QA Team",
    "slug": "qa-team",
    "logo": null
  },
  "starterAddressId": "addr_123",
  "seededSampleEmailCount": 2,
  "starterInboxProvisioned": true
}

List organization stats

GET/api/organizations/stats

Return aggregate per-organization counts for the authenticated user across organizations they belong to.

Auth
Authenticated endpoint. It is user-scoped rather than organization-scoped.
Success
200

Request headers

Request headers
HeaderExampleRequirementDetails
Cookie session or X-API-KeyCookie: <better-auth session> or X-API-Key: spin_...RequiredAll documented product endpoints require an authenticated user session or a valid Better Auth API key.

Success response

Success response
FieldTypeDetails
itemsArray<object>

One item per organization the current user belongs to.

items[].organizationIdstring

Organization identifier.

items[].memberCountnumber

Number of members in the organization.

items[].addressCountnumber

Number of email addresses in the organization.

items[].emailCountnumber

Number of stored emails in the organization.

Common error responses

Common error responses
StatusErrorWhen it happens
401unauthorizedThe request is not authenticated.
403email verification requiredThe authenticated user has not verified their email address.

Example request

BashRequest to list organization counts
curl "https://api.spinupmail.com/api/organizations/stats" \
  -H "X-API-Key: spin_..."

Example response

JSONOrganization-level member, address, and email counts
{
  "items": [
    {
      "organizationId": "org_abc123",
      "memberCount": 3,
      "addressCount": 12,
      "emailCount": 241
    }
  ]
}

Get email activity

GET/api/organizations/stats/email-activity

Return organization-scoped daily email counts grouped in the requested timezone.

Auth
Authenticated and organization-scoped. API key requests must include X-Org-Id.
Success
200
  • The days parameter is clamped to the range 1-30 and defaults to 14.
  • Invalid timezone values return a 400 error instead of falling back silently.

Request headers

Request headers
HeaderExampleRequirementDetails
Cookie session or X-API-KeyCookie: <better-auth session> or X-API-Key: spin_...RequiredAll documented product endpoints require an authenticated user session or a valid Better Auth API key.
X-Org-Idorg_abc123OptionalRequired for API key requests on org-scoped endpoints. Session-cookie requests can use the active organization on the session instead.

Query parameters

Query parameters
FieldTypeRequirementDetails
daysstringOptional

Requested number of recent days to include in the chart response.

Default: 14

Constraints: Clamped to 1-30.

timezonestringOptional

IANA timezone used to bucket daily counts, such as Europe/Istanbul or America/New_York.

Default: UTC

Success response

Success response
FieldTypeDetails
timezonestring

Resolved timezone used in the response.

dailyArray<object>

One entry per day in the selected date window.

daily[].datestring

Calendar day key in YYYY-MM-DD format.

daily[].countnumber

Number of emails received on that day.

Common error responses

Common error responses
StatusErrorWhen it happens
400x-org-id header is required for api key usageAn API key request omits X-Org-Id.
400active organization is requiredA session request has no active organization and does not pass X-Org-Id.
400invalid timezonetimezone is provided but is not a valid IANA timezone.
401unauthorizedThe request is not authenticated.
403forbiddenThe authenticated principal does not belong to the requested organization.

Example request

BashRequest daily email activity for an organization
curl --get "https://api.spinupmail.com/api/organizations/stats/email-activity" \
  -H "X-API-Key: spin_..." \
  -H "X-Org-Id: org_abc123" \
  --data-urlencode "days=7" \
  --data-urlencode "timezone=Europe/Istanbul"

Example response

JSONDaily email counts in the selected timezone
{
  "timezone": "Europe/Istanbul",
  "daily": [
    { "date": "2026-03-02", "count": 4 },
    { "date": "2026-03-03", "count": 2 },
    { "date": "2026-03-04", "count": 0 }
  ]
}

Get email summary

GET/api/organizations/stats/email-summary

Return organization-level aggregate email and attachment statistics used by dashboard summaries.

Auth
Authenticated and organization-scoped. API key requests must include X-Org-Id.
Success
200

Request headers

Request headers
HeaderExampleRequirementDetails
Cookie session or X-API-KeyCookie: <better-auth session> or X-API-Key: spin_...RequiredAll documented product endpoints require an authenticated user session or a valid Better Auth API key.
X-Org-Idorg_abc123OptionalRequired for API key requests on org-scoped endpoints. Session-cookie requests can use the active organization on the session instead.

Success response

Success response
FieldTypeDetails
totalEmailCountnumber

Total emails stored for the organization.

attachmentCountnumber

Total attachments stored for the organization.

attachmentSizeTotalnumber

Total attachment bytes stored for the organization.

attachmentSizeLimitnumber

Resolved per-organization attachment storage cap in bytes.

topDomainsArray<object>

Most frequent sender domains.

topDomains[].domainstring

Sender domain.

topDomains[].countnumber

Email count for that sender domain.

busiestInboxesArray<object>

Inboxes with the highest email counts.

busiestInboxes[].addressIdstring

Inbox identifier.

busiestInboxes[].addressstring

Inbox email address.

busiestInboxes[].countnumber

Email count for the inbox.

dormantInboxesArray<object>

Inboxes with no recent activity but existing records.

dormantInboxes[].addressIdstring

Inbox identifier.

dormantInboxes[].addressstring

Inbox email address.

dormantInboxes[].createdAtstring | null

When the dormant inbox was created.

Constraints: ISO 8601 timestamp when present.

Common error responses

Common error responses
StatusErrorWhen it happens
400x-org-id header is required for api key usageAn API key request omits X-Org-Id.
400active organization is requiredA session request has no active organization and does not pass X-Org-Id.
401unauthorizedThe request is not authenticated.
403forbiddenThe authenticated principal does not belong to the requested organization.

Example request

BashRequest organization email summary metrics
curl "https://api.spinupmail.com/api/organizations/stats/email-summary" \
  -H "X-API-Key: spin_..." \
  -H "X-Org-Id: org_abc123"

Example response

JSONOrganization email and attachment summary metrics
{
  "totalEmailCount": 241,
  "attachmentCount": 19,
  "attachmentSizeTotal": 483210,
  "attachmentSizeLimit": 104857600,
  "topDomains": [
    { "domain": "github.com", "count": 32 }
  ],
  "busiestInboxes": [
    { "addressId": "addr_123", "address": "signup@spinupmail.dev", "count": 44 }
  ],
  "dormantInboxes": [
    { "addressId": "addr_456", "address": "old-flow@spinupmail.dev", "createdAt": "2026-02-10T09:15:00.000Z" }
  ]
}