> ## Documentation Index
> Fetch the complete documentation index at: https://docs.remark.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GraphQL API

> Programmatic access to your Remark data via GraphQL using User API Keys

Enterprise customers can use User API Keys to programmatically query their Remark data via the GraphQL API. This covers dashboard statistics, conversation history, smart tags, order data, and more.

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="#authentication">
    Create and use API keys to authenticate requests.
  </Card>

  <Card title="Dashboard Statistics" icon="chart-line" href="#dashboard-statistics">
    Query chat volume, revenue, conversion rates, and leaderboards.
  </Card>

  <Card title="Conversations" icon="message-square" href="#conversation-listing">
    List conversations, fetch details, and inspect associated customer events.
  </Card>

  <Card title="Smart Tags" icon="tags" href="#smart-tag-aggregation">
    Aggregate smart tag counts across conversations.
  </Card>
</CardGroup>

***

## Authentication

### Creating an API Key

<Steps>
  <Step title="Navigate to API Keys">
    Go to **Settings > Personal API Keys** in the Remark dashboard.
  </Step>

  <Step title="Create a key">
    Click **Create API Key**, give it a name and optional expiry date.
  </Step>

  <Step title="Copy the token">
    Copy the token immediately — it is only shown once. Token format: `rmrk_u_` followed by a secure hash.
  </Step>
</Steps>

### Using the API Key

All requests go to the GraphQL endpoint:

```
POST https://api.withremark.com/graphql
```

Include your token in the `Authorization` header:

```
Authorization: Bearer rmrk_u_your_token_here
```

The key inherits the full permissions of the user who created it. Keys can be revoked at any time from the dashboard.

### Example Request

```bash theme={null}
curl -X POST https://api.withremark.com/graphql \
  -H "Authorization: Bearer rmrk_u_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ vendorById(vendorId: \"your-vendor-id\") { name } }"
  }'
```

***

## Dashboard Statistics

The main analytics entry point. All stats are nested under `vendorById > conversationAggregateStatistics` and support filtering by date range and optionally by channel.

<Note>
  **Permission required:** VIEW\_CONVERSIONS (for revenue/conversion fields)
</Note>

### All-in-One Dashboard Query

Fetches the most commonly used KPIs in a single request:

```graphql theme={null}
query DashboardStats(
  $vendorId: ID!
  $start: DateTime!
  $end: DateTime!
  $timezone: String!
  $channel: ConversationChannel
) {
  vendorById(vendorId: $vendorId) {
    conversationAggregateStatistics(start: $start, end: $end, channel: $channel) {
      # Chat volume
      numberOfChats
      numberOfAIChats
      numberOfHumanChats
      automationRate

      # Revenue & conversions
      totalValueOfConversationsAttributedToChat
      totalValueOfConversationsAttributedToHumanChat
      totalValueOfConversationsAttributedToAIChat
      numberOfConversionsAttributed
      conversionRateForChatters
      conversionRateForNonChatters
      aovForChatters
      aovForNonChatters
      aovForAttributedConversions
      aovForNonAttributedConversions

      # Performance
      averageExpertResponseTime
      medianJoinTime
      totalManHoursSpentInChat

      # Time series
      attributedRevenueTimeSeries(timezone: $timezone) {
        precision
        time
        totalAttributedValue
      }
      chatsByHourHistogram(timezone: $timezone) {
        hour
        count
        average
      }
      chatsByDayOfWeekHistogram(timezone: $timezone) {
        day
        dayIndex
        chatCount
        dayCount
      }

      # Leaderboards
      topProductsByAttributedSales {
        numberOfRecommendations
        numberSold
        totalSales
        product { id name }
      }
      topExpertByAttributedSales {
        asAiPersona
        attributedAmountUSD
        expert { id firstName lastName }
      }
      topExperts(first: 10) {
        asAiPersona
        numberOfChats
        isCSAgent
        expert { id firstName lastName }
      }
      topCitedKnowledge {
        count
        knowledgeDocument { id title }
      }

      # Impact
      minutesSavedByRemark

      # CSAT (requires VIEW_NPS)
      csatScore
    }
  }
}
```

#### Variables

| Variable   | Type                  | Required | Description                                                                               |
| ---------- | --------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `vendorId` | `ID!`                 | Yes      | Your vendor ID                                                                            |
| `start`    | `DateTime!`           | Yes      | Period start (ISO 8601)                                                                   |
| `end`      | `DateTime!`           | Yes      | Period end (ISO 8601)                                                                     |
| `timezone` | `String!`             | Yes      | IANA timezone for time series (e.g. `"America/New_York"`)                                 |
| `channel`  | `ConversationChannel` | No       | Filter by channel: `RemarkChat`, `GorgiasTicket`, `ZendeskTicket`, `KustomerConversation` |

#### Example Response

```json theme={null}
{
  "data": {
    "vendorById": {
      "conversationAggregateStatistics": {
        "numberOfChats": 1842,
        "numberOfAIChats": 1455,
        "numberOfHumanChats": 387,
        "automationRate": 0.79,
        "totalValueOfConversationsAttributedToChat": 284500.00,
        "numberOfConversionsAttributed": 156,
        "conversionRateForChatters": 0.085,
        "conversionRateForNonChatters": 0.032,
        "aovForChatters": 185.50,
        "aovForNonChatters": 142.00,
        "averageExpertResponseTime": 45,
        "medianJoinTime": 12,
        "attributedRevenueTimeSeries": [
          { "precision": "DAY", "time": "2026-03-01T00:00:00Z", "totalAttributedValue": 9500.00 },
          { "precision": "DAY", "time": "2026-03-02T00:00:00Z", "totalAttributedValue": 11200.00 }
        ],
        "topProductsByAttributedSales": [
          {
            "numberOfRecommendations": 89,
            "numberSold": 34,
            "totalSales": 6120.00,
            "product": { "id": "prod_1", "name": "Trail Runner Pro" }
          }
        ],
        "minutesSavedByRemark": 48500,
        "csatScore": 4.6
      }
    }
  }
}
```

<AccordionGroup>
  <Accordion title="Field Reference">
    | Field                                            | Description                                         |
    | ------------------------------------------------ | --------------------------------------------------- |
    | `numberOfChats`                                  | Total conversations in period                       |
    | `numberOfAIChats`                                | Conversations handled entirely by AI                |
    | `numberOfHumanChats`                             | Conversations involving a human expert              |
    | `automationRate`                                 | Fraction of chats handled by AI (0–1)               |
    | `totalValueOfConversationsAttributedToChat`      | Total revenue attributed to chat interactions (USD) |
    | `totalValueOfConversationsAttributedToHumanChat` | Revenue attributed to human-handled chats           |
    | `totalValueOfConversationsAttributedToAIChat`    | Revenue attributed to AI-handled chats              |
    | `numberOfConversionsAttributed`                  | Number of orders attributed to chat                 |
    | `conversionRateForChatters`                      | Conversion rate for visitors who chatted            |
    | `conversionRateForNonChatters`                   | Conversion rate for visitors who did not chat       |
    | `aovForChatters`                                 | Average order value for chatters (USD)              |
    | `aovForNonChatters`                              | Average order value for non-chatters (USD)          |
    | `aovForAttributedConversions`                    | AOV for orders directly attributed to chat          |
    | `aovForNonAttributedConversions`                 | AOV for non-attributed orders                       |
    | `averageExpertResponseTime`                      | Average expert response time (seconds)              |
    | `medianJoinTime`                                 | Median time for an expert to join a chat (seconds)  |
    | `totalManHoursSpentInChat`                       | Total expert hours spent in chat                    |
    | `attributedRevenueTimeSeries`                    | Revenue over time, bucketed by day/week/month       |
    | `chatsByHourHistogram`                           | Chat volume by hour of day                          |
    | `chatsByDayOfWeekHistogram`                      | Chat volume by day of week                          |
    | `topProductsByAttributedSales`                   | Products ranked by attributed revenue               |
    | `topExpertByAttributedSales`                     | Single top expert by revenue                        |
    | `topExperts`                                     | Experts ranked by chat count                        |
    | `topCitedKnowledge`                              | Most-referenced knowledge documents                 |
    | `minutesSavedByRemark`                           | Estimated minutes saved by AI automation            |
    | `csatScore`                                      | Average CSAT score (requires VIEW\_NPS)             |
  </Accordion>
</AccordionGroup>

### Period-over-Period Comparison

Use GraphQL aliases to fetch current and previous periods in one request:

```graphql theme={null}
query DashboardComparison(
  $vendorId: ID!
  $start: DateTime!
  $end: DateTime!
  $prevStart: DateTime!
  $prevEnd: DateTime!
) {
  vendorById(vendorId: $vendorId) {
    current: conversationAggregateStatistics(start: $start, end: $end) {
      numberOfChats
      totalValueOfConversationsAttributedToChat
      conversionRateForChatters
      aovForChatters
      automationRate
    }
    previous: conversationAggregateStatistics(start: $prevStart, end: $prevEnd) {
      numberOfChats
      totalValueOfConversationsAttributedToChat
      conversionRateForChatters
      aovForChatters
      automationRate
    }
  }
}
```

***

## Smart Tag Aggregation

Count how often each smart tag was applied to conversations in a time range. Useful for tracking trending topics, common customer issues, or product interest over time.

<Note>
  **Permission required:** VIEW\_CONVERSATIONS
</Note>

### Count Tags in a Period

```graphql theme={null}
query SmartTagSummary($vendorId: ID!, $start: DateTime!, $end: DateTime!) {
  smartTagCounts(vendorId: $vendorId, start: $start, end: $end) {
    tag {
      id
      title
      description
    }
    count
  }
}
```

#### Example Response

```json theme={null}
{
  "data": {
    "smartTagCounts": [
      {
        "tag": { "id": "tag_1", "title": "Sizing Question", "description": "Customer asked about product sizing" },
        "count": 234
      },
      {
        "tag": { "id": "tag_2", "title": "Return Request", "description": "Customer wants to return a product" },
        "count": 89
      }
    ]
  }
}
```

### List Available Smart Tags

```graphql theme={null}
query VendorTags($vendorId: ID!) {
  vendorSmartTags(vendorId: $vendorId) {
    id
    title
    description
  }
}
```

***

## Conversation Listing

Paginated list of conversations with filtering and sorting. Uses cursor-based pagination.

<Note>
  **Permission required:** VIEW\_CONVERSATIONS
</Note>

```graphql theme={null}
query Conversations(
  $vendorId: ID!
  $first: Int
  $cursor: ID
  $sort: [ConversationSortInput!]
  $filter: [ConversationFilterInput!]
) {
  conversations(
    vendorId: $vendorId
    first: $first
    cursor: $cursor
    sort: $sort
    filter: $filter
  ) {
    edges {
      cursor
      node {
        id
        status
        channel
        platform
        subject
        created
        lastMessageDate
        updated
        customerIdentity {
          id
          suppliedFirstName
          email           # requires VIEW_PII
        }
        currentExpert {
          id
          firstName
          lastName
        }
        hasAttributedConversion(anyExpert: true)
        hasPositiveNPS(anyExpert: true)
        vendorConversationSummaries {
          summary
        }
        mostRecentSegmentStats {
          smartTags { id title }
          aiAppliedTags { tag weight explanation }
          aiInferredSentiment
          pairingTime
          totalTimeSpentChatting
        }
      }
    }
    totalCount
    hasNextPage
    hasPrevPage
    startCursor
    endCursor
  }
}
```

### Variables

| Variable   | Type                         | Required | Description                                                                         |
| ---------- | ---------------------------- | -------- | ----------------------------------------------------------------------------------- |
| `vendorId` | `ID!`                        | Yes      | Your vendor ID                                                                      |
| `first`    | `Int`                        | No       | Page size (default varies)                                                          |
| `cursor`   | `ID`                         | No       | Cursor from previous page's `endCursor`                                             |
| `sort`     | `[ConversationSortInput!]`   | No       | Sort criteria. For stable pagination, pass `[{ field: created, direction: desc }]`. |
| `filter`   | `[ConversationFilterInput!]` | No       | Filter criteria                                                                     |

### Filter Options

| Filter Key         | Type                    | Description                                |
| ------------------ | ----------------------- | ------------------------------------------ |
| `expertId`         | `ID`                    | Filter by assigned expert                  |
| `email`            | `String`                | Search by customer email                   |
| `conversionStatus` | `Enum`                  | `Attributed`, `Converted`, `None`          |
| `smartTagIds`      | `[ID]`                  | Filter by smart tag IDs                    |
| `npsRating`        | `Enum`                  | `Positive`, `Negative`                     |
| `sentiment`        | `Enum`                  | AI-inferred sentiment                      |
| `expertChatType`   | `Enum`                  | `AI`, `Human`, `Both`, `HasAI`, `HasHuman` |
| `channels`         | `[ConversationChannel]` | Filter by channel type                     |

### Sort Options

| Field             | Direction                                                                                 |
| ----------------- | ----------------------------------------------------------------------------------------- |
| `created`         | `desc` (newest conversation first) or `asc`. Recommended for paginating long result sets. |
| `lastMessageDate` | `desc` (most recently active first) or `asc`. Used when no sort is supplied.              |

<Note>
  For exports, backfills, and other long pagination jobs, explicitly sort by
  `created`. The default sort is `lastMessageDate`, which can change when an
  older conversation receives a new message and may make cursor pagination less
  stable over time.
</Note>

### Recommended Variables

```json theme={null}
{
  "vendorId": "your-vendor-id",
  "first": 50,
  "sort": [{ "field": "created", "direction": "desc" }]
}
```

### Pagination

Use cursor-based pagination to iterate through results:

```graphql theme={null}
# First page
conversations(
  vendorId: "..."
  first: 50
  sort: [{ field: created, direction: desc }]
) { ... }

# Next page
conversations(
  vendorId: "..."
  first: 50
  cursor: "endCursor_from_previous"
  sort: [{ field: created, direction: desc }]
) { ... }
```

***

## Conversation Detail

Fetch a single conversation with its full message history.

<Note>
  **Permission required:** VIEW\_CONVERSATIONS
</Note>

```graphql theme={null}
query ConversationDetail($id: ID!, $last: Int!) {
  conversation(id: $id) {
    id
    channel
    platform
    subject
    status
    created
    updated
    externalUrl
    customerIdentity {
      id
      suppliedFirstName
      email               # requires VIEW_PII
      location
    }
    hasAttributedConversion(anyExpert: true)
    hasPositiveNPS(anyExpert: true)
    vendorConversationSummaries { summary }
    npsSurveys {                          # requires VIEW_NPS
      id
      rating
      expert { id firstName lastName }
    }
    mostRecentSegmentStats {
      smartTags { id title description }
      aiAppliedTags { tag weight explanation }
      aiInferredSentiment
      pairingTime
      totalTimeSpentChatting
    }
    events(last: $last) {
      totalCount
      hasNextPage
      hasPrevPage
      endCursor
      startCursor
      edges {
        cursor
        node {
          id
          body
          type
          created
          expert { id firstName lastName }
          detectedIntents { id name explanation }
          actionsTaken { name success }
          recommendations {
            product { id name }
            variant { id name }
          }
          knowledgeCitations {
            knowledgeDocument { id title }
          }
        }
      }
    }
  }
}
```

***

## Conversation Customer Events

Use `customerEvent` to fetch the browsing and shopping events associated with a
conversation's session. This is the right place to look for product views,
category page views, searches, and cart activity that happened around a chat.

<Note>
  **Permission required:** VIEW\_CONVERSATIONS
</Note>

```graphql theme={null}
query ConversationCustomerEvents($conversationId: ID!, $vendorId: ID!) {
  customerEvent(id: $conversationId, vendorId: $vendorId) {
    id
    created
    type
    name
    search
    product {
      id
      externalId
      name
      brandName
      externalUrl
    }
    cart {
      id
    }
  }
}
```

### Event Types

| Type                     | Meaning                                      | Useful fields                                                                                  |
| ------------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `ProductPageView`        | Shopper viewed a product detail page         | `product.id`, `product.externalId`, `product.name`, `product.brandName`, `product.externalUrl` |
| `CategoryPageView`       | Shopper viewed a category or collection page | `name`                                                                                         |
| `PageView`               | Shopper viewed a non-product page            | `created`, `type`                                                                              |
| `Search`                 | Shopper searched the site                    | `search`                                                                                       |
| `CartAdd` / `CartRemove` | Shopper changed their cart                   | `product`, `cart`                                                                              |

***

## Session & Order Data

Look up browsing sessions and conversion/order details for a customer.

<Note>
  **Permission required:** VIEW\_CONVERSIONS
</Note>

```graphql theme={null}
query SessionDetails($vendorId: ID!, $lead: String!) {
  sessionByVendorId(vendorId: $vendorId, lead: $lead) {
    id
    lastUserAgent {
      browser
      deviceType
      deviceModel
    }
    conversions(last: 10) {
      edges {
        node {
          id
          created
          totalPrice
          currency
          orderNumber
          externalId
          externalUrl
          platform
          attributionMethod
          lineItems {
            quantity
            finalTotalPrice
            product { id name }
            variant { id name }
          }
          attribution {
            method
            chattingExpert { id firstName lastName }
          }
        }
      }
    }
  }
}
```

#### Key Fields

| Field                        | Description                               |
| ---------------------------- | ----------------------------------------- |
| `totalPrice`                 | Order total                               |
| `attributionMethod`          | How the conversion was attributed to chat |
| `attribution.chattingExpert` | The expert credited with the sale         |
| `lineItems`                  | Individual products/variants in the order |

***

## Permissions Reference

Your API key inherits the permissions of the user who created it. Different data requires different permissions:

| Permission          | Grants access to                                                   |
| ------------------- | ------------------------------------------------------------------ |
| VIEW\_CONVERSATIONS | Conversation data, smart tags, message history, chat analysis      |
| VIEW\_CONVERSIONS   | Revenue metrics, conversion rates, order details, attributed sales |
| VIEW\_PII           | Customer emails, names, IP addresses, contact info                 |
| VIEW\_NPS           | NPS/CSAT scores and survey details                                 |
| VIEW\_BILLING       | Billing and payment data                                           |
| EDIT\_ANALYTICS     | Smart tag management (create/edit/delete)                          |

An admin or vendor owner will have all permissions for their vendor.

***

## Rate Limits & Best Practices

* **Rate limiting:** Rate limits may be enforced to prevent abuse and maintain quality of service
* **Request only what you need:** GraphQL lets you select specific fields — smaller queries are faster
* **Use pagination:** Always paginate conversation and session lists rather than fetching everything at once
* **Cache where appropriate:** Dashboard statistics don't change in real time; polling every few minutes is sufficient
* **Time ranges:** Always provide `start`/`end` for dashboard stats queries — omitting them returns lifetime data which is slower
* **Timezone:** Pass your local IANA timezone (e.g. `America/New_York`) for time series and histogram queries to get correctly bucketed data
