# API Concepts

Understand the architecture, authentication, and core resources

## API Architecture Overview

WEIR provides two distinct API surfaces designed for different use cases:

### External API

For third-party developers building integrations.

`https://wapi.weir.ai/`

- Read access to public licenses
- Full CRUD on owned resources
- Webhook subscriptions

### Internal API

For first-party WEIR applications.

`https://id.weir.ai/api-v2/internal/v2/`

- User authentication methods
- Profile & settings management
- Claims & earnings access

## Core Domain Concepts

### Identities

People whose likeness can be licensed. Identities have legal names (private), display/stage names (public), verification status, and reference images for detection.

### Licenses

Rights packages defining how an identity can be used. Each license specifies terms, restrictions, pricing, and status (draft, private, public, archived). **Licenses are the primary resource in the API.**

### Mentions

Discovered content that may reference a licensed identity. Found through text matching (name detection) or image matching (visual identity detection). Mentions have confidence scores and statuses: pending, confirmed, rejected, or flagged.

### Webhooks

Real-time notifications when events occur: license.created, license.published, mention.detected, subscription.started, and more. Configure endpoints to receive HMAC-signed payloads.

## Authentication Model

### API Key Types

### Domain Keys

`weir_domain_`

For server-to-server and web applications.

- • Optional domain restriction
- • No bundle ID required
- • Higher rate limits available

### App Keys

`weir_app_`

For iOS and Android applications.

- • Bundle ID binding required
- • Admin verification required
- • X-Bundle-ID header required

### Token Lifecycle

Exchange

API Key + Secret

Access Token

1 hour lifetime

Refresh Token

30 day lifetime

### Scopes

`license:read` — View licenses

`license:write` — Create/update

`mention:read` — View mentions

`mention:write` — Manage mentions

`webhook:manage` — Manage webhooks

`user:read` — View profile

Legacy plural forms (e.g., `licenses:read`) are still supported for backward compatibility.

## Response Format

All API responses use a consistent **light envelope pattern**:

SuccessError

```json
{
  "success": true,
  "data": { ... },
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalItems": 150,
    "totalPages": 8,
    "hasMore": true
  }
}
```

Every response includes an `X-Request-ID` header. Include this when contacting support for faster issue resolution.

## Rate Limiting

| Key Type      | Limit      | Burst           |
|---------------|------------|------------------|
| Domain Key    | 1,000/hour | 100/minute     |
| App Key       | 500/hour   | 50/minute      |
| Public Metadata| 60/minute  | 10/second      |

Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers.

## Token Security

Proper token storage is critical for application security. Different tokens have different sensitivity levels and storage requirements.

### Token Classification

| Token         | Sensitivity | Storage Requirement       |
|---------------|-------------|---------------------------|
| API Key       | High (static)| Secure backend only       |
| API Secret    | Critical    | Backend only, encrypted at rest |
| Access Token   | Medium (1hr) | Secure memory or encrypted storage |
| Refresh Token  | High (30 days)| Must be stored securely  |

### Mobile Storage Best Practices

#### iOS

### iOS Keychain Services

Use `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` for refresh tokens:

```swift
import Security

func storeRefreshToken(_ token: String) {
    let data = token.data(using: .utf8)!
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: "weir_refresh_token",
        kSecAttrService as String: "com.yourapp.weir",
        kSecValueData as String: data,
        kSecAttrAccessible as String:
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly
    ]
    SecItemDelete(query as CFDictionary)
    SecItemAdd(query as CFDictionary, nil)
}
```

### Security Best Practices

**Never ship API secrets** in mobile apps—use backend token exchange

**Clear tokens on logout** from all secure storage

**Refresh proactively**—refresh tokens ~5 minutes before expiration

**Handle 401 gracefully**—prompt re-authentication when tokens are revoked

**Use certificate pinning** in mobile apps to prevent MITM attacks

## Token Compatibility

Internal and External API tokens are **not interchangeable**. Each API surface has its own token type with different capabilities and storage mechanisms.

| Question                                                           | Answer                                                                                                                                |
|--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| Can I use an Internal token on External API?                      | No<br>Internal tokens are stored in `weir_api_access_tokens` and issued via passkey/magic-link/password. External API only accepts tokens from `weir_api_tokens`. |
| Can I use an External token on Internal API?                      | No<br>External tokens have limited scopes (`license:read`, `mention:read`, `webhook:manage`) and cannot access internal endpoints like `/profile`, `/claims`, `/earnings`. |
| Can I use a Supabase JWT directly?                                 | Internal API Only<br>The Internal API accepts Supabase JWTs via token exchange and direct validation. The External API does not accept Supabase JWTs. |
| Why are they separate?                                             | **Security isolation.** Internal API is for first-party apps with full user context; External API is for third-party integrations with restricted scopes. |

### Token Flow Diagram

```
┌───────────────────────────────────────────────────────────────┐
│                     TOKEN COMPATIBILITY                        │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│   INTERNAL TOKENS                    EXTERNAL TOKENS          │
│   (weir_api_access_tokens)           (weir_api_tokens)        │
│   ┌─────────────────────┐           ┌──────────────────┐      │
│   │ Issued via:         │           │ Issued via:      │      │
│   │ • Passkey           │           │ • API key +      │      │
│   │ • Magic Link        │           │   secret         │      │
│   │ • Password          │           │                  │      │
│   │ • Supabase JWT      │           │                  │      │
│   └─────────────────────┘           └──────────────────┘      │
│            │                               │                  │
│            ▼                               ▼                  │
│   ┌─────────────────────┐           ┌──────────────────┐      │
│   │ INTERNAL API        │    ✗      │ EXTERNAL API     │      │
│   │ /internal/v2/...    │ ◄─────────│ /v2/...          │      │
│   │                     │           │                  │      │
│   │ Full access:        │    ✗      │ Limited scopes:  │      │
│   │ • profile           │ ─────────►│ • license:read   │      │
│   │ • claims            │           │ • mention:read   │      │
│   │ • earnings          │           │ • webhook:manage │      │
│   │ • settings          │           │                  │      │
│   └─────────────────────┘           └──────────────────┘      │
│                                                               │
│   ✗ = Token type NOT accepted on this API                     │
└───────────────────────────────────────────────────────────────┘
```

If you're building a third-party integration, use the **External API** with domain/app keys. If you're building a WEIR mobile app or first-party client, use the **Internal API** with service tokens.
