# Credential Activity Tracking

The Credential Activity system provides unified, event-sourced tracking of all credential issuance and claim lifecycle events. It enables partners to have a clear audit trail and analytics for credentials they've issued.

## Overview

When credentials are sent through the LearnCard Network, the system automatically logs activity events that track the full lifecycle:

```
CREATED → DELIVERED → CLAIMED
              ↓
          EXPIRED / FAILED
```

## Event Types

| Event       | Description                                                  |
| ----------- | ------------------------------------------------------------ |
| `CREATED`   | Credential created and sent to inbox (email/phone recipient) |
| `DELIVERED` | Credential delivered to profile (profile ID/DID recipient)   |
| `CLAIMED`   | Recipient claimed/accepted the credential                    |
| `EXPIRED`   | Credential expired before being claimed                      |
| `FAILED`    | Credential delivery failed                                   |

## Activity Record Structure

Each activity record contains:

```typescript
interface CredentialActivity {
    id: string;              // Unique event ID
    activityId: string;      // Groups related events for same issuance
    eventType: string;       // CREATED | DELIVERED | CLAIMED | EXPIRED | FAILED
    timestamp: string;       // ISO timestamp
    actorProfileId: string;  // Who triggered the action
    recipientType: string;   // 'profile' | 'email' | 'phone'
    recipientIdentifier: string;
    boostUri?: string;       // Associated boost template
    credentialUri?: string;  // Credential URI (for profile sends)
    inboxCredentialId?: string; // Inbox credential ID (for inbox sends)
    integrationId?: string;  // Integration attribution
    source: string;          // Entry point (see Source Types below)
    metadata?: object;       // Additional context
}
```

## Understanding Activity IDs

The `activityId` is a key concept that links related events in a credential's lifecycle. Each time a credential is sent, a new unique `activityId` is generated. When that credential is later claimed, the CLAIMED event shares the same `activityId`.

### Example: Same Boost Sent Multiple Times

```
Boost "Employee Badge"
├── send() to Alice → activityId: "abc123"
│   ├── DELIVERED (timestamp 1)
│   └── CLAIMED (timestamp 2) ← same activityId "abc123"
│
├── send() to Bob → activityId: "def456"  
│   ├── DELIVERED (timestamp 3)
│   └── CLAIMED (timestamp 4) ← same activityId "def456"
│
└── send() to Alice (again) → activityId: "ghi789"
    └── DELIVERED (timestamp 5) ← different activityId even for same recipient
```

**Key points:**

* `activityId` tracks a **single issuance lifecycle**, not a boost or recipient
* Same boost sent twice = two different `activityId`s
* Same recipient receiving twice = two different `activityId`s
* The `boostUri` field links activities to the boost template for aggregate stats

## Source Types

The `source` field indicates how the credential was issued:

| Source             | Description                                    |
| ------------------ | ---------------------------------------------- |
| `send`             | Via the unified `send()` route                 |
| `sendBoost`        | Via the `sendBoost` route                      |
| `sendCredential`   | Via the `sendCredential` route                 |
| `inbox`            | Claimed from Universal Inbox                   |
| `claim`            | Profile-to-profile credential acceptance       |
| `claimLink`        | Claimed via a claim link (combined send+claim) |
| `contract`         | Issued via ConsentFlow contract                |
| `acceptCredential` | Credential accepted by recipient               |

## API Endpoints

### Get My Activities

Retrieve a paginated list of credential activities for the authenticated profile.

```http
GET /activity/credentials
```

**Query Parameters:**

* `limit` (number, default: 25) - Max results per page
* `cursor` (string) - Pagination cursor
* `boostUri` (string) - Filter by boost template
* `eventType` (string) - Filter by event type
* `integrationId` (string) - Filter by integration

**Response:**

```json
{
    "records": [
        {
            "id": "abc123",
            "activityId": "xyz789",
            "eventType": "DELIVERED",
            "timestamp": "2024-01-15T10:30:00Z",
            "recipientType": "profile",
            "recipientIdentifier": "alice",
            "boostUri": "lc:network:example.com/trpc:boost:123",
            "boost": {
                "id": "123",
                "name": "Achievement Badge",
                "category": "Achievement"
            },
            "recipientProfile": {
                "profileId": "alice",
                "displayName": "Alice Smith"
            }
        }
    ],
    "hasMore": true,
    "cursor": "2024-01-15T10:30:00Z"
}
```

### Get Activity Stats

Retrieve aggregated statistics for credential activities.

```http
GET /activity/credentials/stats
```

**Query Parameters:**

* `boostUris` (string\[]) - Filter by boost templates
* `integrationId` (string) - Filter by integration

**Response:**

```json
{
    "total": 150,
    "created": 50,
    "delivered": 100,
    "claimed": 75,
    "expired": 5,
    "failed": 2,
    "claimRate": 75.0
}
```

### Get Activity by ID

Retrieve details of a specific activity by its activity ID.

```http
GET /activity/credentials/{activityId}
```

### Get Activity Chain

Retrieve all events in an activity chain by `activityId`. Returns events ordered chronologically (oldest first) to show the full lifecycle of a credential issuance.

```http
GET /activity/credentials/{activityId}/chain
```

**Response:**

```json
[
    {
        "id": "event-1",
        "activityId": "xyz789",
        "eventType": "DELIVERED",
        "timestamp": "2024-01-15T10:30:00Z",
        "source": "send",
        ...
    },
    {
        "id": "event-2",
        "activityId": "xyz789",
        "eventType": "CLAIMED",
        "timestamp": "2024-01-15T11:45:00Z",
        "source": "acceptCredential",
        ...
    }
]
```

This is useful for tracking the complete lifecycle of a single credential issuance from delivery through claim.

## Usage Examples

### JavaScript/TypeScript

```typescript
// Get recent activity
const activity = await learnCard.invoke.getMyActivities({
    limit: 10,
});

console.log(`Total activities: ${activity.records.length}`);

// Get stats for a specific boost
const stats = await learnCard.invoke.getActivityStats({
    boostUris: ['lc:network:example.com/trpc:boost:123'],
});

console.log(`Claim rate: ${stats.claimRate}%`);
```

### Filtering by Event Type

```typescript
// Get only claimed credentials
const claimed = await learnCard.invoke.getMyActivities({
    eventType: 'CLAIMED',
    limit: 50,
});
```

## Best Practices

1. **Use `activityId` to track lifecycle** - Related events share the same `activityId`, making it easy to track a credential from send to claim.
2. **Poll with timestamps** - When polling for new activity, use the `cursor` parameter to efficiently fetch only new events.
3. **Filter by boost for template-specific analytics** - Use `boostUri` to get stats for specific credential templates.
4. **Monitor claim rates** - The `claimRate` in stats helps identify which credentials are being claimed and which may need follow-up.

## Integration with Notifications

Activity events are separate from the notification system. While notifications push real-time updates to recipients, activity records provide a persistent audit trail for issuers.

For real-time updates, consider using webhooks configured during credential issuance.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.learncard.com/core-concepts/network-and-interactions/credential-activity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
