---
title: Admin Reference
path: reference/admin
status: published
---

# Admin Reference

Admin endpoints — tenants, partners, roles, permissions, group mappings, sender domains, and tracking settings. These are ScaiSend-native surfaces, not SendGrid-compatible.

**Base path:** `/api/admin/`
**Auth:** JWT-only for most endpoints. API keys can read a narrow subset (`domains.read`, `domains.write` for domain endpoints; `admin.settings` for tracking settings).

## Partners

### GET /api/admin/partners

List partners. A tenant-scoped JWT returns only its own partner.

**Response (200):**

```json
{
  "data": [
    {"id": "prt_acme", "name": "Acme Inc", "slug": "acme"}
  ]
}
```

### GET /api/admin/partners/{partner_id}

Get a single partner. `partner_id` can be the local UUID or the ScaiKey ID (`prt_xxx`).

**Response (200):** `{"data": {...}}`.

## Tenants

### GET /api/admin/tenants

List tenants.

**Query parameters:**

| Parameter | Notes |
|-----------|-------|
| `partner_id` | Filter by partner (UUID or `prt_xxx`) |

Non-admin users see only their own tenant. Partner-admin users see every tenant in their partner.

**Response (200):**

```json
{
  "data": [
    {
      "id": "tnt_01HXYZ",
      "scaikey_id": "tnt_acme",
      "name": "Acme Corp",
      "slug": "acme-corp",
      "partner_id": "prt_01HXYZ",
      "settings": {}
    }
  ]
}
```

### GET /api/admin/tenants/{tenant_id}

Get a tenant. `tenant_id` may be the local UUID or the ScaiKey ID.

**Response (200):** `{"data": {...}}`.

### PATCH /api/admin/tenants/{tenant_id}

Update a tenant's name or settings.

**Request body:**

| Field | Notes |
|-------|-------|
| `name` | New display name |
| `settings` | Arbitrary JSON |

### GET /api/admin/tenants/{tenant_id}/tracking

Get tracking settings for a tenant.

**Response (200):**

```json
{
  "data": {
    "open_tracking_enabled": true,
    "click_tracking_enabled": true,
    "click_tracking_enable_text": false,
    "subscription_tracking_enabled": false,
    "subscription_tracking_text": "",
    "subscription_tracking_html": "",
    "subscription_tracking_substitution_tag": "",
    "subscription_tracking_landing_html": "",
    "ganalytics_enabled": false,
    "ganalytics_utm_source": "",
    "ganalytics_utm_medium": "",
    "ganalytics_utm_campaign": "",
    "ganalytics_utm_term": "",
    "ganalytics_utm_content": "",
    "image_embed_mode": "proxy"
  }
}
```

### PUT /api/admin/tenants/{tenant_id}/tracking

Update tracking settings. Partial — unspecified fields keep their value.

**Request body:** any subset of the response fields above.

**Response (200):** updated settings.

**Permission:** `admin.settings`.

## Roles and permissions

### GET /api/admin/permissions

List all permissions.

**Query parameters:**

| Parameter | Notes |
|-----------|-------|
| `category` | Filter by category |

**Response (200):**

```json
[
  {"id": "perm_mail_send", "name": "mail.send", "description": "Send emails", "category": "mail"}
]
```

### GET /api/admin/permissions/{permission_id}

Get a single permission.

### GET /api/admin/roles

List roles.

**Query parameters:**

| Parameter | Notes |
|-----------|-------|
| `include_permissions` | If true, embed the permissions array |

**Response (200):**

```json
[
  {
    "id": "role_admin",
    "name": "admin",
    "description": "Full administrative access",
    "is_system": false,
    "permissions": [...]
  }
]
```

### POST /api/admin/roles

Create a role.

**Request body:**

| Field | Type | Required |
|-------|------|---------|
| `name` | string | Yes |
| `description` | string | No |
| `permission_ids` | array of string | No |

**Response (201):** role object.

### GET /api/admin/roles/{role_id}

Get a role with permissions.

### PATCH /api/admin/roles/{role_id}

Update name or description.

### DELETE /api/admin/roles/{role_id}

Delete a role. Users holding this role lose these permissions.

### PUT /api/admin/roles/{role_id}/permissions

Replace the role's permission set.

**Request body:** `{"permission_ids": ["perm_mail_send", "perm_stats_read"]}`

**Response (200):** list of permissions now on the role.

### POST /api/admin/roles/{role_id}/permissions/{permission_id}

Add a single permission.

### DELETE /api/admin/roles/{role_id}/permissions/{permission_id}

Remove a single permission.

## User roles

### GET /api/admin/users/{user_id}/roles

List roles held by a user.

**Response (200):** `[RoleResponse]`.

### POST /api/admin/users/{user_id}/roles/{role_id}

Assign a role to a user.

**Response (204):** no body.

### DELETE /api/admin/users/{user_id}/roles/{role_id}

Remove a role assignment.

**Response (204):** no body.

## Group mappings

### GET /api/admin/group-mappings

List ScaiKey group-to-role mappings.

**Response (200):**

```json
[
  {
    "id": "gm_01HXYZ",
    "group_id": "grp_01HXYZ",
    "group_name": "Engineering",
    "scaikey_group_id": "grp_engineering",
    "role_id": "role_developer",
    "role_name": "developer",
    "created_at": "2026-04-23T10:00:00Z"
  }
]
```

### POST /api/admin/group-mappings

Create a mapping.

**Request body:**

| Field | Required |
|-------|---------|
| `group_id` | Yes |
| `role_id` | Yes |

### DELETE /api/admin/group-mappings/{mapping_id}

Delete a mapping.

## Sender domains

### GET /api/admin/domains

List domains.

**Query parameters:**

| Parameter | Notes |
|-----------|-------|
| `verified_only` | Return only verified domains |
| `page` | 1-indexed |
| `page_size` | Items per page |

**Response (200):**

```json
[
  {
    "id": "dom_01HXYZ",
    "domain": "mail.example.com",
    "verified": true,
    "verified_at": "2026-04-23T10:00:00Z",
    "dkim_selector": "scaisend",
    "dmarc_policy": "quarantine",
    "dmarc_rua_email": "dmarc@example.com",
    "is_shared": false,
    "is_active": true,
    "created_at": "2026-04-23T09:00:00Z"
  }
]
```

### POST /api/admin/domains

Add a domain. ScaiSend generates DKIM keypair and returns DNS records to publish.

**Request body:**

| Field | Required | Notes |
|-------|---------|-------|
| `domain` | Yes | Fully qualified domain |
| `is_shared` | No | If true, usable by any tenant under this partner |
| `dmarc_policy` | No | `none`, `quarantine`, or `reject` |
| `dmarc_rua_email` | No | Where DMARC aggregate reports go |

**Response (201):**

```json
{
  "id": "dom_01HXYZ",
  "domain": "mail.example.com",
  "verified": false,
  "dkim_selector": "scaisend",
  "dns_records": [
    {"type": "TXT", "host": "scaisend._domainkey.mail.example.com", "value": "v=DKIM1; ..."},
    {"type": "TXT", "host": "mail.example.com", "value": "v=spf1 ..."},
    {"type": "TXT", "host": "_dmarc.mail.example.com", "value": "v=DMARC1; ..."}
  ]
}
```

**Permission:** `domains.write`.

### GET /api/admin/domains/{domain_id}

Get a domain with DNS records.

### PATCH /api/admin/domains/{domain_id}

Update domain fields.

**Request body:** any subset of:

| Field | Notes |
|-------|-------|
| `is_active` | Deactivate to prevent further sends |
| `is_shared` | Toggle shared flag |
| `dmarc_policy` | Change policy |
| `dmarc_rua_email` | Change report destination |

### DELETE /api/admin/domains/{domain_id}

Delete a domain. Future sends from this domain fail.

### POST /api/admin/domains/{domain_id}/verify

Run the DNS verification check.

**Response (200):**

```json
{
  "domain": "mail.example.com",
  "verified": true,
  "dkim_verified": true,
  "spf_verified": true,
  "txt_verified": true,
  "errors": []
}
```

**Permission:** `domains.write`.

### POST /api/admin/domains/{domain_id}/rotate-dkim

Generate a new DKIM keypair under a new selector. Old selector remains active until explicitly removed.

**Response (200):**

```json
{
  "domain": "mail.example.com",
  "new_selector": "scaisend2",
  "dkim_public_key": "v=DKIM1; k=rsa; p=...",
  "dns_records": [...]
}
```

### GET /api/admin/domains/{domain_id}/dns-records

Get the list of DNS records currently required. Idempotent — use if you lost the original POST response.

**Response (200):**

```json
[
  {"type": "TXT", "host": "scaisend._domainkey.mail.example.com", "value": "v=DKIM1; ..."},
  {"type": "TXT", "host": "mail.example.com", "value": "v=spf1 ..."},
  {"type": "TXT", "host": "_dmarc.mail.example.com", "value": "v=DMARC1; ..."}
]
```

## Related

- [Multi-tenancy](../concepts/multi-tenancy)
- [Roles and Permissions](../concepts/roles-and-permissions)
- [Sender Domains](../concepts/sender-domains)
- [Tracking](../concepts/tracking)
