# Mypa

> Personal communication hub. Send tezits to family/team members, manage messages, get briefings, interrogate context, and share mirrors. Voice-first with Library of Context preservation and Tezit Protocol support.

- Skill: `diegosouzapw/mypa` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/mypa`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/mypa/raw
- Safety review: pending (external: skill-scanner WARNING, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/mypa

---


# MyPA Skill

MyPA is a voice-first personal communication hub. Every message is a **Tez** (short for Tezit) — a discrete, actionable item with its full context iceberg preserved forever. This skill lets you send messages to teammates, manage your tez feed, get briefings, interrogate context, and share mirrors.

## Quick Start for External OpenClaw

This skill works with **any OpenClaw runtime**, not just the MyPA Canvas UI.

### 1. Install
```bash
cp SKILL.md ~/.openclaw/workspace/skills/mypa/SKILL.md
```

### 2. Set Environment
```bash
export MYPA_API_URL="https://api.mypa.chat"   # or your instance URL
export MYPA_EMAIL="you@example.com"
export MYPA_PASSWORD="your-password"
# Optional: for PA-to-PA messaging
export RELAY_URL="https://relay.tezit.com"     # or your relay URL
```

### 3. Verify
```bash
# Login
TOKEN=$(curl -s -X POST "$MYPA_API_URL/api/auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"$MYPA_EMAIL\",\"password\":\"$MYPA_PASSWORD\"}" | jq -r '.data.tokens.accessToken')

# Bootstrap — discover teams, capabilities, endpoints
curl -s "$MYPA_API_URL/api/auth/bootstrap" -H "Authorization: Bearer $TOKEN" | jq .
```

If you see `data.user` and `data.teams` in the response, you're connected.

## Bootstrap Flow (CRITICAL for External Runtimes)

On first contact with a MyPA instance, **always run the bootstrap sequence**:

1. **Login** → `POST /api/auth/login` → store `accessToken` + `refreshToken`
2. **Bootstrap** → `GET /api/auth/bootstrap` → discover:
   - `teams[]` — all teams the user belongs to
   - `capabilities` — which integrations are available (relay, crm, paWorkspace, federation, scheduler)
   - `instanceMode` — `"team"` or `"personal"`
   - `connectedHubs` — (personal mode only) federated team hubs
   - `endpoints` — backend URL, relay URL, cross-team URL
3. **If `teams.length > 1`** → present the list and ask the user which team to scope to
4. **If `teams.length === 1`** → set scope automatically
5. **Store the resolved team scope** in conversation memory for subsequent operations

```bash
curl "$MYPA_API_URL/api/auth/bootstrap" \
  -H "Authorization: Bearer $TOKEN"
```

**Response shape:**
```json
{
  "data": {
    "user": { "id": "uuid", "email": "...", "name": "...", "department": "...", "teamId": "uuid" },
    "teams": [{ "id": "uuid", "name": "Alpha Team", "role": "member", "isActive": true, "memberCount": 4 }],
    "instanceMode": "team",
    "capabilities": { "relay": true, "crm": false, "paWorkspace": false, "federation": true, "scheduler": false },
    "connectedHubs": [],
    "endpoints": { "backend": "https://api.mypa.chat", "relay": "http://10.108.0.2:3002", "crossTeam": null }
  }
}
```

## Multi-Team Scope Resolution (CRITICAL)

Users may belong to multiple teams. Write operations **must** be scoped to a specific team.

### Rules
- **Read operations** (context, briefing, feed, library search): use the user's active team by default. In personal mode, can aggregate across all teams.
- **Write operations** (create team card, classify, relay share): **MUST** have an explicit `teamId` when the user belongs to 2+ teams.
- **Disambiguation protocol**: if `teams.length > 1` and no teamId is specified, the backend returns `AMBIGUOUS_TEAM_SCOPE` with the list of teams. Present this to the user and ask them to choose.

### Handling AMBIGUOUS_TEAM_SCOPE

If a write operation returns HTTP 400 with `error.code === "AMBIGUOUS_TEAM_SCOPE"`:

```json
{
  "error": {
    "code": "AMBIGUOUS_TEAM_SCOPE",
    "message": "User is a member of multiple teams. Specify teamId explicitly.",
    "teams": [
      { "id": "team-1-uuid", "name": "Alpha Team", "role": "member" },
      { "id": "team-2-uuid", "name": "Beta Team", "role": "admin" }
    ]
  }
}
```

**Agent response**: Present the team list and ask the user which team they mean. Then retry the operation with the selected `teamId`.

### Scope Modes
- **`team:{teamId}`** — scoped to a specific team (default for write operations)
- **`personal`** — local data only (user's personal tezits, no team context)
- **`all-teams`** — read-only aggregation across all connected teams (personal instance mode only, via cross-team endpoints)

## Authentication

All requests require a JWT Bearer token. Obtain one by logging in.

**Login:**
```bash
curl -X POST "$MYPA_API_URL/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email": "'"$MYPA_EMAIL"'", "password": "'"$MYPA_PASSWORD"'"}'
```

Store `accessToken` and `refreshToken` from the response. Include on every request:
```
Authorization: Bearer $TOKEN
```

Access tokens expire after **15 minutes**. On 401, refresh:
```bash
curl -X POST "$MYPA_API_URL/api/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refreshToken": "'"$REFRESH_TOKEN"'"}'
```

## Optional CRM Environment

If Twenty CRM is configured for this deployment, set:

```bash
export TWENTY_API_URL="http://localhost:3000"
export TWENTY_API_KEY="your_twenty_api_key"
```

Use CRM calls only when both are present. If missing, continue with MyPA-only workflows.

Optional PA Workspace bridge for Google execution flows:

```bash
export PA_WORKSPACE_API_URL="http://localhost:3003"
```

When set, CRM orchestration can optionally execute PA email/calendar actions in addition to Tez draft generation.

## CRM + Tez Operations

**IMPORTANT:** Before any CRM operation, check CRM status first. If `configured: true` and `reachable: true`, proceed with CRM calls. If not, inform the user that CRM is not yet set up.

### CRM Intent Routing (CRITICAL)

Treat CRM as an API integration (Twenty), not a local workspace file.

When a user asks things like:
- "add this person to CRM"
- "update that deal"
- "show my contacts/opportunities/tasks"

You MUST:
1. Call `GET /api/crm/status` first.
2. If `data.configured === true` and `data.reachable === true`, execute CRM API calls (`/api/crm/people`, `/api/crm/opportunities`, `/api/crm/tasks`).
3. If not configured/reachable, tell the user CRM integration is not configured or unreachable and ask for setup help (URL/key), not for a file path.

You MUST NOT:
- ask the user for a "CRM file", "CRM folder", or local CRM path as the primary approach.
- claim CRM is unavailable just because no local file exists.

Example user request:
> "Add my wife, Natalie Williams to our CRM."

Expected action flow:
1. `GET /api/crm/status`
2. If healthy, `POST /api/crm/people` with structured name:
```bash
curl -X POST "$MYPA_API_URL/api/crm/people" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payload":{"name":{"firstName":"Natalie","lastName":"Williams"}}}'
```
3. Confirm creation result to user.

### Check CRM + runtime status

```bash
curl "$MYPA_API_URL/api/crm/status" \
  -H "Authorization: Bearer $TOKEN"
```

```bash
curl "$MYPA_API_URL/api/crm/workflows/status" \
  -H "Authorization: Bearer $TOKEN"
```

### List CRM entities

```bash
curl "$MYPA_API_URL/api/crm/people?limit=20" \
  -H "Authorization: Bearer $TOKEN"
```

```bash
curl "$MYPA_API_URL/api/crm/opportunities?limit=20" \
  -H "Authorization: Bearer $TOKEN"
```

```bash
curl "$MYPA_API_URL/api/crm/tasks?limit=20" \
  -H "Authorization: Bearer $TOKEN"
```

### Write CRM entities (create/update)

**Create a person** — `name` is a structured `{firstName, lastName}` object. Use `emails.primaryEmail` for email.
```bash
curl -X POST "$MYPA_API_URL/api/crm/people" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payload":{"name":{"firstName":"Jane","lastName":"Example"},"emails":{"primaryEmail":"jane@example.com"},"jobTitle":"VP Sales","city":"New York","phones":{"primaryPhoneNumber":"+15551234567"}}}'
```

**Update a person** — PATCH with only the fields to change:
```bash
curl -X PATCH "$MYPA_API_URL/api/crm/people/<entity-id>" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payload":{"jobTitle":"CTO","city":"San Francisco"}}'
```

**Update a task:**
```bash
curl -X PATCH "$MYPA_API_URL/api/crm/tasks/<task-id>" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payload":{"status":"in_progress"}}'
```

### Build Tez-ready context from CRM

```bash
curl -X POST "$MYPA_API_URL/api/crm/tez-context" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entityType":"opportunity","entityId":"<id>"}'
```

Use `data.relayContext` with relay `/tez/share` or `/conversations/:id/messages`.

### Cross-system coordination workflow

```bash
curl -X POST "$MYPA_API_URL/api/crm/workflows/coordinate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entityType":"opportunity",
    "entityId":"<id>",
    "objective":"Move deal to next stage with clear owner actions",
    "tez":{"teamId":"<team-id>","recipients":["<user-id>"]},
    "openclaw":{"enabled":true},
    "googleWorkspace":{"enabled":true,"dryRun":true}
  }'
```

This returns:
- `tezDraft` (ready for relay `/tez/share`)
- `openclaw` planning summary
- `googleWorkspace` draft/execute result (dry-run by default)

## Key Concepts

**Tez-Based Model:** Everything is a tez. Tezits are discrete, actionable items — not threads.

**Personal vs Team Tezits:**
- "Message for Me" → personal tez (private note, reminder). Only the creator sees it.
- "Message for Team" → team tez sent to specific recipients or all team members.

**Library of Context:** ALL original content (voice transcripts, typed text) is preserved forever. Context is sacred — never suggest deleting it.

**Status State Machine:**
```
pending → active → resolved → archived
```
`archived` is terminal. Transitions are enforced by the API.

## Sending Messages (CRITICAL)

### Step 0: Resolve Team Scope

Before any write operation, ensure you have a resolved team scope:
- If user is in **1 team** → scope is set automatically (no `teamId` needed)
- If user is in **2+ teams** → you MUST include `teamId` in write requests
- If unsure, check `teams[]` from the bootstrap response

### Step 1: Classify Intent

**ALWAYS classify first** before creating a tez. This determines whether the message is for the user themselves, a specific teammate, or the whole team.

For multi-team users, pass `teamId` to scope name matching to the correct team:

```bash
curl -X POST "$MYPA_API_URL/api/cards/classify" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Tell Ros I love her", "teamId": "team-uuid"}'
```

**Response:**
```json
{
  "data": {
    "intent": "dm",
    "recipientId": "f23cdac6-...",
    "recipientName": "Rosalind Price",
    "confidence": 98,
    "reason": "First name match \"Rosalind Price\" with directive"
  },
  "meta": { "teamSize": 3 }
}
```

**Intent values:**
- `"self"` → Create a personal tez (`POST /api/cards/personal`)
- `"dm"` → Create a team tez directed to the specific recipient
- `"broadcast"` → Create a team tez for all team members (set `shareToTeam: true`)

### Step 2: Check Auto-Send Preference

After classifying, check the user's `paPreferences.autoSendDMs` setting (available in the PA context). The rules:
- If `autoSendDMs` is **true**, confidence is **≥ 90**, intent is `"dm"`, and team size is **≤ 10**: send immediately without confirmation.
- Otherwise: show the user who you're sending to and ask for confirmation before creating the tez.

### Step 3: Create the Tez

**Personal Tez (intent: "self"):**
```bash
curl -X POST "$MYPA_API_URL/api/cards/personal" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Remember to buy milk", "summary": "Buy milk"}'
```

**Team Tez (intent: "dm" or "broadcast"):**
```bash
curl -X POST "$MYPA_API_URL/api/cards/team" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "I love you!",
    "summary": "Love note to Ros",
    "recipients": ["f23cdac6-..."],
    "teamId": "team-uuid"
  }'
```

**Team broadcast (intent: "broadcast"):**
```bash
curl -X POST "$MYPA_API_URL/api/cards/team" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Reminder: standup is at 10am today.",
    "summary": "Standup @ 10am",
    "shareToTeam": true,
    "teamId": "team-uuid"
  }'
```

**Parameters:**
- `content` (string, required) — The message content
- `summary` (string, optional) — Short summary; fallback to truncated content
- `recipients` (string[], optional) — Targeted message to specific teammates
- `shareToTeam` (boolean, optional) — Explicit team-wide broadcast. If `true`, server expands recipients to all teammates.
- `teamId` (string, optional) — Required for multi-team users. Single-team users can omit.
- `dueDate` (string, optional) — ISO 8601 datetime

**Rule:** For team cards, you must provide either `recipients[]` or `shareToTeam: true` (no silent broadcast).

**IMPORTANT:** Treat Tez as the canonical system of record. External channels (WhatsApp/Telegram/email/etc) are optional and depend on configured relay channel providers. If not configured, deliver via Tez (team cards or relay) and share mirrors when needed.

## Search & Feed

### Feed (Primary View)

```bash
curl "$MYPA_API_URL/api/cards/feed?sort=priority&status=pending&limit=10" \
  -H "Authorization: Bearer $TOKEN"
```

**Query parameters:**
- `sort` — `priority` (default) or `chronological`
- `status` — `pending`, `active`, `resolved`, `archived`, or `all`
- `limit` — Max 50 (default 20)
- `cursor` — For pagination

### Library Search

Search all preserved original content.

```bash
curl "$MYPA_API_URL/api/library/search?q=dinner+plans" \
  -H "Authorization: Bearer $TOKEN"
```

## Tez Details & Actions

### Get Details
```bash
curl "$MYPA_API_URL/api/cards/:id" \
  -H "Authorization: Bearer $TOKEN"
```

### Change Status
```bash
curl -X PATCH "$MYPA_API_URL/api/cards/:id" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}'
```

Valid transitions: `pending→active`, `pending→resolved`, `pending→archived`, `active→resolved`, `active→archived`, `resolved→archived`.

### Respond to a Tez
```bash
curl -X POST "$MYPA_API_URL/api/cards/:id/respond" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "On it!"}'
```

### Snooze
```bash
curl -X POST "$MYPA_API_URL/api/cards/:id/snooze" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"until": "2026-02-08T09:00:00Z"}'
```

### Archive
```bash
curl -X DELETE "$MYPA_API_URL/api/cards/:id" \
  -H "Authorization: Bearer $TOKEN"
```

## PA Context (ALWAYS FETCH FIRST)

**Before answering any question about the user's messages, team, or workload, ALWAYS fetch context first.**

```bash
curl "$MYPA_API_URL/api/pa/context" \
  -H "Authorization: Bearer $TOKEN"
```

**Response includes:**
- `userName` — The user's name
- `teamName` — Active team name
- `teamMembers` — Array of `{ id, name, roles, skills, department }` — **these are the people the user can message**
- `pendingCardCount` — Number of pending tezits
- `topPriorityCard` — Most recent pending tez
- `recentCards` — Last 30 days of tezits with status and response counts
- `paPreferences` — User's PA settings including `autoSendDMs`, `model`, `tone`, `responseStyle`
- `teams` — All teams the user belongs to

**Multi-team note:** The `teams` array shows ALL teams the user belongs to. `teamMembers` shows members of the **active** team only. For multi-team users, the user may need to switch active team scope before operations.

**CRITICAL:** The `teamMembers` array tells you who the user can send messages to. When the user says "tell Ros...", match "Ros" against `teamMembers[].name`. Use the classify endpoint for reliable matching.

## Briefing

```bash
curl "$MYPA_API_URL/api/pa/briefing" \
  -H "Authorization: Bearer $TOKEN"
```

Returns: `pendingCount`, `activeCount`, `resolvedToday`, `topPriorityCards`, `staleCards`, `upcomingDeadlines`.

## Tez Mirror (External Sharing)

Generate a lossy, read-only summary of a tez for sharing outside the app (SMS, email, clipboard).

### Render Preview
```bash
curl -X POST "$MYPA_API_URL/api/tez/:cardId/mirror" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"template": "surface"}'
```

**Templates:**
- `"teaser"` (~50 chars) — Push notification / SMS preview
- `"surface"` (~200 chars) — Email / group chat sharing
- `"surface_facts"` (~500 chars) — Sharing with someone who needs context

### Log Share (Audit)
```bash
curl -X POST "$MYPA_API_URL/api/tez/:cardId/mirror/send" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"template": "surface", "destination": "sms"}'
```

## Tezit Protocol

### Interrogate a Tez
Ask questions answered strictly from the tez's transmitted context.

```bash
curl -X POST "$MYPA_API_URL/api/tez/:cardId/interrogate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"question": "What was decided about dinner?"}'
```

### Get Citations
```bash
curl "$MYPA_API_URL/api/tez/:cardId/citations" \
  -H "Authorization: Bearer $TOKEN"
```

### Export as Tezit Bundle
```bash
curl "$MYPA_API_URL/api/tez/:cardId/export" \
  -H "Authorization: Bearer $TOKEN"
```

### Export as Portable Tez Bundle (Level 2)
Portable bundles are designed to be moved between systems (export/import, backups, external sharing).

```bash
curl "$MYPA_API_URL/api/tez/:cardId/export/portable" \
  -H "Authorization: Bearer $TOKEN"
```

### Fork a Tez (Counter-Argument)
```bash
curl -X POST "$MYPA_API_URL/api/tez/:cardId/fork" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"forkType": "counter", "content": "Actually, I think..."}'
```

Fork types: `counter`, `extension`, `reframe`, `update`.

## Web Research Pattern (Browser → Tez)

When the user asks you to research something on the web:
1. Ask whether this should be a **private note** or a **team share** (and who the recipients are).
2. Use your browser tool to gather sources. Capture at least: `title`, `url`, and `accessedAt` date.
3. Produce a short **surface** summary the user can act on.
4. Preserve the full context iceberg as Tez context (facts + artifacts + constraints).

### Share research as a personal Tez (private, preserved in Library)
Use `contextLayers` to preserve the research notes and sources alongside the Tez.

```bash
curl -X POST "$MYPA_API_URL/api/cards/personal" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Research summary: competitor pricing tiers and key differentiators",
    "summary": "Competitor pricing research",
    "contextLayers": [
      {
        "type": "assistant",
        "query": "Research competitor pricing and summarize with sources",
        "content": "Findings:\\n- Tier A: ...\\n- Tier B: ...\\n\\nSources (accessed 2026-02-10):\\n- Example Pricing Page: https://example.com/pricing\\n- Example Docs: https://example.com/docs"
      }
    ]
  }'
```

### Share research as a relay Tez (DM or team-visible)
Use relay `context[]` for structured layers (`background`, `fact`, `artifact`, `constraint`, etc). Put sources in `artifact` layers and set confidence/source for facts.

```bash
curl -X POST "$RELAY_URL/tez/share" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "teamId": "team-uuid",
    "visibility": "team",
    "surfaceText": "Competitor X pricing: 3 tiers; biggest gap is seat-based minimums.",
    "type": "update",
    "urgency": "normal",
    "recipients": [],
    "context": [
      { "layer": "background", "content": "Goal: compare pricing + packaging vs our current plan." },
      { "layer": "fact", "content": "Tier 1 starts at $Y/month (seat-based).", "confidence": 90, "source": "verified" },
      { "layer": "constraint", "content": "Numbers may change; verify before sending externally." },
      { "layer": "artifact", "content": "Source: Competitor pricing page (accessed 2026-02-10) https://example.com/pricing" }
    ]
  }'
```

## Tez Sharing (External Access via Share Tokens)

Share tokens grant scoped, read-only guest access to interrogate a specific Tez's context. The sender controls what's visible, how many interrogations are allowed, and can revoke at any time.

### Create a Share Token
```bash
curl -X POST "$MYPA_API_URL/api/tez/:cardId/share" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "For Alice",
    "contextScope": "surface",
    "maxInterrogations": 10,
    "expiresInHours": 48
  }'
```

**Context scope options:**
- `"surface"` — Card content + summary only. TIP answers from surface text alone.
- `"full"` — All context items visible. Full interrogation capability.
- `"selected"` — Only specific context items (pass `contextItemIds` array).

**Response:**
```json
{
  "data": {
    "token": "raw-token-shown-once",
    "shareUrl": "https://mypa.chat/tez/abc123?token=...",
    "tokenId": "uuid",
    "expiresAt": "2026-02-11T10:00:00Z"
  }
}
```

**IMPORTANT:** The raw token is shown ONLY once. Store or share it immediately.

### List Share Tokens
```bash
curl "$MYPA_API_URL/api/tez/:cardId/shares" \
  -H "Authorization: Bearer $TOKEN"
```

Returns all tokens for the card (without raw token values). Use to manage active shares.

### Update Scope (Share More / Pull Back)
```bash
curl -X PATCH "$MYPA_API_URL/api/tez/:cardId/share/:tokenId" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"contextScope": "full"}'
```

This is the mechanism for sharing more context or pulling it back. Change `contextScope`, add/remove `contextItemIds`, or adjust `maxInterrogations`.

### Revoke a Share Token
```bash
curl -X DELETE "$MYPA_API_URL/api/tez/:cardId/share/:tokenId" \
  -H "Authorization: Bearer $TOKEN"
```

Immediately revokes the token. Any future use returns 401.

## Tezit Relay (PA-to-PA Messaging)

When the Tezit channel plugin is active, the agent can send and receive native Tez messages through the relay. The relay URL is available as `$RELAY_URL` when configured.

**Multi-team note:** The `teamId` in relay calls must match the user's resolved team scope. For multi-team users, always pass the explicit `teamId` from the bootstrap/scope resolution step.

### Send a Tez via Relay
```bash
curl -X POST "$RELAY_URL/tez/share" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "teamId": "team-uuid",
    "surfaceText": "The budget decision: approved $50k for Q2",
    "type": "decision",
    "urgency": "high",
    "recipients": ["recipient-uuid"],
    "context": [
      {"layer": "background", "content": "Q2 planning concluded last week"},
      {"layer": "fact", "content": "$50,000 approved for marketing spend", "confidence": 100, "source": "stated"}
    ]
  }'
```

### Check Relay Unread
```bash
curl "$RELAY_URL/unread" \
  -H "Authorization: Bearer $TOKEN"
```

### Conversations (DM + Group)

Use conversations when the user wants a familiar chat experience (WhatsApp/Slack-style) rather than discrete Tez broadcasts.

**Create a DM (exactly 2 people total):**
```bash
curl -X POST "$RELAY_URL/conversations" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"dm","memberIds":["other-user-uuid"]}'
```

**Create a group (2+ people total, requires `name`):**
```bash
curl -X POST "$RELAY_URL/conversations" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"group","name":"Project Alpha","memberIds":["user-a-uuid","user-b-uuid"]}'
```

**List my conversations:**
```bash
curl "$RELAY_URL/conversations" \
  -H "Authorization: Bearer $TOKEN"
```

**Send a message in a conversation:**
```bash
curl -X POST "$RELAY_URL/conversations/<conversation-id>/messages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "surfaceText":"Quick update: shipped the fix, waiting on CI.",
    "type":"update",
    "urgency":"normal",
    "context":[
      {"layer":"background","content":"Bug was in invite accept upsert."},
      {"layer":"artifact","content":"PR: https://github.com/org/repo/pull/123"}
    ]
  }'
```

**Mark conversation read:**
```bash
curl -X POST "$RELAY_URL/conversations/<conversation-id>/read" \
  -H "Authorization: Bearer $TOKEN"
```

### Register Contact with Channel Routing
```bash
curl -X POST "$RELAY_URL/contacts/register" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Alice Smith",
    "email": "alice@example.com",
    "phone": "+1234567890",
    "preferredChannel": "whatsapp",
    "channels": ["tezit", "whatsapp", "email"]
  }'
```

**Channel routing fields:**
- `channels` — Ordered fallback chain. First reachable channel wins. e.g. `["tezit", "email", "whatsapp"]`
- `preferredChannel` — Explicit override. If set, always try this first.
- `phone` — Phone number for SMS/WhatsApp/Telegram delivery.
- `telegramId` — Telegram user ID for Telegram delivery.

**Routing logic (for agent):**
1. Check `preferredChannel` — if set and available, use it.
2. Otherwise, iterate `channels` array in order. First channel the recipient is reachable on wins.
3. If recipient has OpenClaw (tezit channel available) → native Tez (full context iceberg preserved).
4. If fallback to lossy channel (WhatsApp, email, etc.) → generate mirror + TIP interrogation link.

## Mirror Settings

### Get Settings
```bash
curl "$MYPA_API_URL/api/settings/mirror" \
  -H "Authorization: Bearer $TOKEN"
```

### Update Settings
```bash
curl -X PATCH "$MYPA_API_URL/api/settings/mirror" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mirrorWarningsEnabled": false}'
```

## Twenty CRM + Tez Workflows (When Configured)

### Check CRM connectivity
```bash
curl "$MYPA_API_URL/api/crm/status" \
  -H "Authorization: Bearer $TOKEN"
```

If `data.configured` and `data.reachable` are both `true`, CRM operations are available.

### Pull CRM object context and share as Tez

1. Fetch CRM object data from MyPA's CRM adapter:
```bash
curl "$MYPA_API_URL/api/crm/people?limit=5" \
  -H "Authorization: Bearer $TOKEN"
```

2. Build Tez-ready context from a CRM object:
```bash
curl -X POST "$MYPA_API_URL/api/crm/tez-context" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entityType":"opportunity","entityId":"opp_123"}'
```

3. Send a team Tez with the returned `contextLayers` attached:
```bash
curl -X POST "$MYPA_API_URL/api/cards/team" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Handoff: priority follow-up for key account",
    "summary": "CRM follow-up handoff",
    "shareIntent": "handoff",
    "contextLayers": [
      {
        "type": "text",
        "content": "CRM opportunity snapshot: opportunity_id=opp_123, title=Renewal Q2, stage=negotiation, next_step=Call by Friday",
        "query": "Explain blockers and next actions for recipient PA"
      }
    ]
  }'
```

### Daily CRM + Tez briefing pattern

1. Pull urgent CRM items (tasks/opportunities due soon).
2. Create one personal tez (`/api/cards/personal`) summarizing top priorities.
3. Create team handoff tezits for delegated items (`shareIntent: "handoff"`).
4. Use `/api/tez/:cardId/interrogate` to answer recipient follow-up questions using attached context only.

## Response Format

**Success:** `{ "data": { ... }, "meta": { "total": 42 } }`
**Error:** `{ "error": { "code": "...", "message": "..." } }`
**Status codes:** 200, 201, 400, 401, 403, 404, 429

## API Contract Examples

The following endpoints have frozen contracts. Breaking changes will break OpenClaw integration.

### GET /api/pa/context

**Request:**
```http
GET /api/pa/context HTTP/1.1
Authorization: Bearer <jwt-token>
```

**Response (200 OK):**
```json
{
  "data": {
    "userId": "uuid",
    "userName": "Alice Smith",
    "teamId": "uuid",
    "teamName": "Smith Family",
    "userRoles": ["engineer", "team_lead"],
    "teams": [
      {
        "id": "uuid",
        "name": "Smith Family",
        "role": "member",
        "isActive": true,
        "memberCount": 4
      }
    ],
    "pendingCardCount": 3,
    "topPriorityCard": {
      "id": "uuid",
      "summary": "Dinner plans tonight",
      "dueDate": "2026-02-08T18:00:00Z",
      "responseCount": 2
    },
    "recentCards": [
      {
        "id": "uuid",
        "summary": "Team meeting notes",
        "status": "resolved",
        "dueDate": null,
        "createdAt": "2026-02-07T10:00:00Z",
        "responseCount": 5,
        "responsePreviews": ["Great points!", "Agreed on timeline"]
      }
    ],
    "teamMembers": [
      {
        "id": "uuid",
        "name": "Bob Smith",
        "roles": ["engineer"],
        "skills": ["python", "docker"],
        "department": "Engineering"
      }
    ],
    "integrations": {
      "openclawConfigured": true,
      "twentyConfigured": true,
      "notificationsEnabled": true
    },
    "paPreferences": {
      "autoSendDMs": true,
      "model": "gpt-4",
      "tone": "warm"
    }
  }
}
```

### GET /api/pa/briefing

**Request:**
```http
GET /api/pa/briefing HTTP/1.1
Authorization: Bearer <jwt-token>
```

**Response (200 OK):**
```json
{
  "data": {
    "pendingCount": 3,
    "activeCount": 2,
    "resolvedToday": 5,
    "topPriorityCards": [
      {
        "id": "uuid",
        "content": "Review Q1 budget",
        "summary": "Budget review",
        "status": "pending",
        "dueDate": "2026-02-09T17:00:00Z",
        "createdAt": "2026-02-08T09:00:00Z",
        "updatedAt": "2026-02-08T09:00:00Z"
      }
    ],
    "staleCards": [],
    "upcomingDeadlines": [
      {
        "id": "uuid",
        "content": "Submit expense report",
        "summary": "Expenses",
        "status": "pending",
        "dueDate": "2026-02-10T23:59:00Z",
        "createdAt": "2026-02-05T14:00:00Z",
        "updatedAt": "2026-02-05T14:00:00Z"
      }
    ]
  }
}
```

### POST /api/cards/classify

**Request:**
```http
POST /api/cards/classify HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "content": "Tell Ros I'll be home at 6pm"
}
```

**Response (200 OK):**
```json
{
  "data": {
    "intent": "dm",
    "recipientId": "f23cdac6-...",
    "recipientName": "Rosalind Price",
    "confidence": 98,
    "reason": "First name match \"Rosalind Price\" with directive"
  },
  "meta": {
    "teamSize": 3
  }
}
```

### POST /api/cards/personal

**Request:**
```http
POST /api/cards/personal HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "content": "Remember to buy milk",
  "summary": "Buy milk",
  "sourceType": "text",
  "dueDate": "2026-02-09T12:00:00Z"
}
```

**Response (201 Created):**
```json
{
  "card": {
    "id": "uuid",
    "content": "Remember to buy milk",
    "summary": "Buy milk",
    "fromUserId": "uuid",
    "status": "pending",
    "sourceType": "text",
    "dueDate": "2026-02-09T12:00:00Z",
    "createdAt": "2026-02-08T10:30:00Z",
    "updatedAt": "2026-02-08T10:30:00Z"
  }
}
```

### POST /api/cards/team

**Request:**
```http
POST /api/cards/team HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "content": "I'll be home at 6pm",
  "summary": "Home at 6",
  "sourceType": "text",
  "recipients": ["f23cdac6-..."]
}
```

**Response (201 Created):**
```json
{
  "card": {
    "id": "uuid",
    "content": "I'll be home at 6pm",
    "summary": "Home at 6",
    "fromUserId": "uuid",
    "status": "pending",
    "sourceType": "text",
    "createdAt": "2026-02-08T10:35:00Z",
    "updatedAt": "2026-02-08T10:35:00Z"
  }
}
```

### GET /api/cards/feed

**Request:**
```http
GET /api/cards/feed?status=pending&limit=10 HTTP/1.1
Authorization: Bearer <jwt-token>
```

**Response (200 OK):**
```json
{
  "cards": [
    {
      "id": "uuid",
      "content": "Review project proposal",
      "summary": "Project review",
      "fromUserId": "uuid",
      "status": "pending",
      "createdAt": "2026-02-08T09:00:00Z",
      "updatedAt": "2026-02-08T09:00:00Z"
    }
  ],
  "meta": {
    "hasMore": false,
    "cursor": null
  }
}
```

### POST /api/tez/:cardId/interrogate

**Request:**
```http
POST /api/tez/abc-123/interrogate HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "question": "What was decided about dinner?",
  "sessionId": "session-uuid"
}
```

**Response (200 OK):**
```json
{
  "data": {
    "answer": "Based on the transmitted context, dinner was decided for 6:30pm at the Italian restaurant downtown.",
    "classification": "answerable",
    "confidence": "high",
    "contextScope": "full",
    "citations": [
      {
        "contextItemId": "ctx-uuid",
        "excerpt": "Let's do 6:30pm at Luigi's downtown",
        "claim": "Dinner time and location were decided"
      }
    ]
  }
}
```

### GET /api/library/search

**Request:**
```http
GET /api/library/search?q=dinner+plans&limit=10 HTTP/1.1
Authorization: Bearer <jwt-token>
```

**Response (200 OK):**
```json
{
  "results": [
    {
      "cardId": "uuid",
      "contextId": "ctx-uuid",
      "content": "Let's plan dinner for Friday night at the new restaurant",
      "snippet": "...plan <b>dinner</b> for Friday night...",
      "rank": 2.45,
      "createdAt": "2026-02-05T14:00:00Z"
    }
  ],
  "meta": {
    "total": 1,
    "query": "dinner plans"
  }
}
```

### Discovery (Public)

No auth required — these power the Tez social network.

**Trending tezits:**
```http
GET {MYPA_API_URL}/api/discover/trending?limit=10&period=7d
```
Returns highest-engagement tezits from the last 7 days. Response:
```json
{
  "data": [
    { "cardId": "uuid", "summary": "...", "senderName": "Alice", "engagementScore": 47, "interrogationCount": 8, "citationCount": 5, "createdAt": "..." }
  ]
}
```

**Platform stats:**
```http
GET {MYPA_API_URL}/api/discover/stats
```
Returns: `{ totalTezits, totalInterrogations, totalCitations, activeUsers, topContributors: [{name, tezCount, engagementScore}] }`

**User profile:**
```http
GET {MYPA_API_URL}/api/discover/profile/{userId}
```
Returns: `{ displayName, memberSince, tezCount, totalEngagement, topTezits: [{cardId, summary, score}] }`

**Share with TIP link (agent convenience):**
```http
POST {MYPA_API_URL}/api/tez/{cardId}/share-with-link
Authorization: Bearer <jwt-token>
Content-Type: application/json

{ "contextScope": "full", "maxInterrogations": 100, "expiresInHours": 168 }
```
Creates a share token and returns full shareable URLs. Used when routing a Tez to a lossy channel (WhatsApp, email, etc.) — include the `interrogateUrl` so the recipient can interrogate the full context.

### Error Response Format

All errors follow this structure:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": {
      "field": "content",
      "issue": "Required field missing"
    }
  }
}
```

**Common error codes:**
- `UNAUTHORIZED` (401) - Missing or invalid auth token
- `FORBIDDEN` (403) - No access to resource
- `NOT_FOUND` (404) - Resource doesn't exist
- `VALIDATION_ERROR` (400) - Invalid request data
- `AMBIGUOUS_TEAM_SCOPE` (400) - Multi-team user must specify teamId (includes `teams[]` in error)
- `NOT_TEAM_MEMBER` (403) - User is not a member of the specified team
- `INTERNAL_ERROR` (500) - Server error

## Tez Transport (Email Delivery via PA)

Send Tez via email from your PA's Google Workspace account. The recipient gets:
- Rich HTML email with Tez content
- `.tez.json` attachment (full bundle for import)
- TIP interrogation link (ask questions about the transmitted context)

**Requirements:**
- PA Workspace must be configured on the server
- User must have a provisioned PA (Google Workspace account)

### Send Tez via Email

```bash
curl -X POST "$MYPA_API_URL/api/tez-transport/send" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fromPaEmail": "alice-pa@mypa.chat",
    "toEmail": "bob@example.com",
    "tezId": "card-uuid",
    "subject": "Optional subject override"
  }'
```

**Parameters:**
- `fromPaEmail` — Your PA's email (e.g., `robertsonprice-pa@mypa.chat`)
- `toEmail` — Recipient's email address
- `tezId` — Card ID to send (fetches bundle from backend)
- `bundle` — OR provide a full Tez bundle directly (skip `tezId` if providing this)
- `subject` — Optional email subject (defaults to Tez summary)

**Response (200 OK):**
```json
{
  "data": {
    "messageId": "google-message-id",
    "fromPaEmail": "alice-pa@mypa.chat",
    "toEmail": "bob@example.com",
    "subject": "Project Update",
    "sentAt": "2026-02-10T01:00:00Z"
  }
}
```

**When to use:**
- Sending Tez to someone without MyPA (external recipient)
- Email is their preferred channel
- You want full context preservation via email (not just lossy text)

**Email contents:**
- HTML body with Tez surface text + context summary
- `X-Tezit-Protocol: 1.2` header (Tez-aware email clients can parse)
- `.tez.json` attachment (full bundle for import/archival)
- TIP link (recipient can interrogate without MyPA account)

### Get Email Send History

```bash
curl "$MYPA_API_URL/api/tez-transport/log?userId=user-uuid&limit=20" \
  -H "Authorization: Bearer $TOKEN"
```

Returns recent email sends from the user's PA.

## Inbound Tez Bridge (Channel Messages → Relay)

When you receive a message from an external channel (WhatsApp, Telegram, email, SMS, etc.), **always record it as a Tez in the relay**. This ensures the PA's eyes are on every message and the Library of Context captures everything.

### Recording an Inbound Channel Message
```bash
curl -X POST "$RELAY_URL/tez/share" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "teamId": "team-uuid",
    "surfaceText": "Message content from the channel",
    "type": "note",
    "sourceChannel": "whatsapp",
    "sourceAddress": "+1234567890",
    "context": [
      {"layer": "background", "content": "Received via WhatsApp from +1234567890 at 2026-02-09T10:30:00Z"}
    ]
  }'
```

**Required fields for bridge:**
- `sourceChannel` — The originating channel: `"whatsapp"`, `"telegram"`, `"email"`, `"sms"`, `"imessage"`, etc.
- `sourceAddress` — The sender's address on that channel (phone number, email, etc.)
- `context` — At minimum, a `"background"` layer noting the source channel and timestamp.

**When to bridge:**
- Every inbound message on a non-Tez channel should be recorded
- Include the original sender identity in `sourceAddress`
- If the message has attachments or rich content, add them as `"artifact"` context layers
- If you can infer the sender's name, add a `"relationship"` context layer

## Guidelines for the Agent

1. **Sending messages = creating tezits.** When the user says "send a message to Ros" or "tell Tom about dinner", classify the intent first, then create a team tez. There is NO external messaging system — tezits ARE messages.

2. **ALWAYS classify before sending.** Use `POST /api/cards/classify` to determine intent and recipient. Never guess who a message is for — the classify endpoint matches names against the team directory.

3. **Fetch PA context first.** Before answering workload questions or sending messages, call `GET /api/pa/context` to know who's on the team and what's pending.

4. **Check autoSendDMs.** If the user's `paPreferences.autoSendDMs` is true and the classify result has ≥90 confidence for a DM in a small team (≤10), send without asking. Otherwise confirm the recipient first.

5. **Context is sacred.** Never suggest deleting context. Only archiving.

6. **Voice-first awareness.** Users may be dictating. Interpret intent generously through transcription artifacts.

7. **B

…(truncated)
