# Story Links API

Machine-to-machine API for minting customer story request links. Authenticated via managed-hosting deploy tokens (server-side only, never in a browser). Use this to programmatically invite customers to share their experience during their subscription lifecycle.

## Authentication

All requests must include your site's managed-hosting deploy token:

```bash
Authorization: Bearer <deploy_token>
# or
x-api-key: <deploy_token>
```

Find your deploy token in your site's Hosting settings in DiscoverWorthy (the same token used for the Business Context API).

This is a server-side credential — never expose it to clients or browsers.

## Discovery

Unauthenticated `GET https://app.discoverworthy.com/api/v1/story-links` returns a machine-readable discovery document listing all endpoints, auth options, and the public docs URL. No token required for this endpoint.

## Endpoints

### POST https://app.discoverworthy.com/api/v1/story-links

Mint a new story link for a customer.

**Request body (JSON):**

```json
{
  "customerName": "Sarah Johnson",
  "customerCompany": "Acme Corp",
  "customerProject": "Website redesign",
  "customerEmail": "sarah@acme.com",
  "externalRef": "ORDER-12345",
  "sendEmail": true
}
```

**Parameters:**

- `customerName` (required): Customer's name. ≤255 characters.
- `customerCompany` (optional): Customer's company. ≤255 characters.
- `customerProject` (optional): Project description. ≤255 characters.
- `customerEmail` (optional): Email address for sending the invitation link. ≤255 characters.
- `externalRef` (optional): Your internal reference number (order ID, subscription ID, etc.). ≤255 characters.
- `sendEmail` (optional): If `true` and customerEmail is provided, automatically send the invitation. Default: `false`.

**Response (201):**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "token": "csr_550e8400e29b41d4a716446655440000",
  "url": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
  "status": "pending",
  "expiresAt": "2026-08-13T12:00:00Z",
  "externalRef": "ORDER-12345",
  "emailSent": true,
  "links": {
    "story": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
    "status": "https://app.discoverworthy.com/api/v1/story-links/csr_550e8400e29b41d4a716446655440000"
  }
}
```

**Rate limiting:**

Each site has a daily cap (default: 50 story links). Minting beyond the cap returns `429 Too Many Requests`.

### GET https://app.discoverworthy.com/api/v1/story-links

List story requests for your site.

**Query parameters:**

- `externalRef` (optional): Filter by external reference (the order/ref number you provided when minting).
- `status` (optional): Filter by stored status: `pending`, `completed`, `cancelled`, or `expired`. (`viewed` is derived — viewed-but-unsubmitted links are stored as `pending` and returned with `status: "viewed"`.)
- `limit` (optional): Results per page. 1–100, default 50.

**Response:**

```json
{
  "requests": [
    {
      "token": "csr_550e8400e29b41d4a716446655440000",
      "status": "viewed",
      "customerName": "Sarah Johnson",
      "customerCompany": "Acme Corp",
      "customerProject": "Website redesign",
      "externalRef": "ORDER-12345",
      "rating": null,
      "createdAt": "2026-07-14T12:00:00Z",
      "viewedAt": "2026-07-14T13:00:00Z",
      "completedAt": null,
      "expiresAt": "2026-08-13T12:00:00Z",
      "emailSent": true,
      "url": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000"
    }
  ]
}
```

### GET https://app.discoverworthy.com/api/v1/story-links/[token]

Get status and completed story for a single link.

**Response (if in progress):**

```json
{
  "token": "csr_550e8400e29b41d4a716446655440000",
  "status": "pending",
  "customerName": "Sarah Johnson",
  "customerCompany": "Acme Corp",
  "customerProject": "Website redesign",
  "externalRef": "ORDER-12345",
  "rating": null,
  "createdAt": "2026-07-14T12:00:00Z",
  "viewedAt": null,
  "completedAt": null,
  "expiresAt": "2026-08-13T12:00:00Z",
  "emailSent": true,
  "url": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
  "story": null,
  "links": {
    "story": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
    "dashboard": "https://app.discoverworthy.com/your-org/case-studies"
  }
}
```

**Response (if completed):**

```json
{
  "token": "csr_550e8400e29b41d4a716446655440000",
  "status": "completed",
  "customerName": "Sarah Johnson",
  "customerCompany": "Acme Corp",
  "customerProject": "Website redesign",
  "externalRef": "ORDER-12345",
  "rating": 9,
  "createdAt": "2026-07-14T12:00:00Z",
  "viewedAt": "2026-07-14T13:00:00Z",
  "completedAt": "2026-07-14T14:00:00Z",
  "expiresAt": "2026-08-13T12:00:00Z",
  "emailSent": true,
  "url": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
  "story": {
    "title": "How Acme Corp increased conversions by 40%",
    "summary": "Sarah shares how the website redesign drove more leads.",
    "challenge": "Outdated site design, poor mobile experience.",
    "solution": "Complete redesign with modern UX and conversion optimizations.",
    "results": "40% increase in conversions, 2x more leads.",
    "testimonial": "The results exceeded our expectations.",
    "testimonialAuthor": "Sarah Johnson",
    "testimonialRole": "Marketing Director",
    "keyMetrics": [
      { "label": "Conversion increase", "value": "40", "unit": "%" },
      { "label": "Lead volume", "value": "2", "unit": "x" }
    ],
    "tags": ["design", "conversion", "website"],
    "servicesUsed": ["UI/UX Design", "Development"],
    "publishStatus": "draft"
  },
  "links": {
    "story": "https://app.discoverworthy.com/story/csr_550e8400e29b41d4a716446655440000",
    "dashboard": "https://app.discoverworthy.com/your-org/case-studies"
  }
}
```

### DELETE https://app.discoverworthy.com/api/v1/story-links/[token]

Rescind a story link (e.g., when the customer's subscription is cancelled). Only pending links can be rescinded.

**Response:**

```json
{
  "message": "Story link rescinded."
}
```

## Status Lifecycle

Story links follow this lifecycle:

| Status | Meaning |
|--------|---------|
| `pending` | Link minted, customer has not yet visited it. |
| `viewed` | Customer clicked the link and started the interview, but did not submit. |
| `completed` | Customer submitted their story. A draft case study is created in your DiscoverWorthy dashboard. |
| `cancelled` | Link was rescinded (e.g., order cancelled). No longer usable. |
| `expired` | Link was not used within 30 days. |

The story remains a **draft** in your dashboard (`publishStatus: "draft"`) until you review and publish it.

## Workflow Example

Order-fulfillment integration:

```bash
# 1. Order placed
curl -X POST https://app.discoverworthy.com/api/v1/story-links \
  -H "Authorization: Bearer <deploy_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "customerName": "Jane Doe",
    "customerEmail": "jane@example.com",
    "externalRef": "ORD-98765",
    "sendEmail": true
  }'

# Save the token returned: "csr_123abc..."

# 2. Poll status periodically (or in your post-fulfillment flow)
curl -X GET "https://app.discoverworthy.com/api/v1/story-links/csr_123abc..." \
  -H "Authorization: Bearer <deploy_token>"

# Check response.status:
# - "completed" → story is ready; review in dashboard and publish
# - "pending" / "viewed" → still waiting for customer
# - "expired" → customer did not respond; consider re-minting or follow-up email

# 3. Order cancelled?
curl -X DELETE "https://app.discoverworthy.com/api/v1/story-links/csr_123abc..." \
  -H "Authorization: Bearer <deploy_token>"
```

## Notes

- The completed story is created as a **draft case study** in your DiscoverWorthy dashboard. You review, edit, and publish it manually in the dashboard's case-studies section (or use the dashboard link provided in the response).
- The customer is guided through a short conversational interview on a branded page — no login or account required on their side.
- Links expire after 30 days and cannot be renewed via the API; mint a new link if needed.
- Daily cap is configurable per environment (default: 50 links/site/day).
