# Visitor

URL: /business/nouns/visitor · Id: `noun:visitor`

> A Visitor is someone a Startup cannot name, known by an id scoped to that Startup.

A Visitor is someone a [Startup](/business/nouns/startup) cannot name, known by an id scoped to that Startup.

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `firstSeenAt` | `datetime` | Yes | When the first request arrived. |

## Object
```json
{
  "$id": "https://api.sb/kilnbakery.com/visitors/visitor_1",
  "$type": "Visitor",
  "firstSeenAt": "2026-10-22T12:00:00Z"
}
```

## Relationships
- <a id="rel-identified-as"></a>A Visitor **is identified as** one [Lead](/business/nouns/lead): a [Lead](/business/nouns/lead), once it gives an address, is tied to every Visitor id it used, one per device. `identifiedAs`, inverse [`identifies`](/business/nouns/lead#rel-identifies).
- <a id="rel-starts"></a>A Visitor **starts** many [Visits](/business/nouns/visit): each [Visit](/business/nouns/visit) is one span of a Visitor’s [Events](/business/nouns/event) on the [Startup](/business/nouns/startup)’s [Sites](/business/nouns/site) and [Apps](/business/nouns/app). `starts`, inverse [`startedBy`](/business/nouns/visit#rel-started-by).

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Visitor.forget](/business/nouns/visitor/forget) | Forget masks a person’s captured data across every Event and Recording and suppresses further capture, keeping the records. | [Privacy Officer](/agents/legal/privacy-officer) | [`Visitor.forgotten`](/business/events/Visitor.forgotten) | One-way (customer): `send`, `do` |
| [Visitor.identify](/business/nouns/visitor/identify) | Identify joins a Visitor to the Contact it turns out to be, so earlier Visits count toward that person. | [Analyst](/agents/marketing/analyst) | [`Visitor.identified`](/business/events/Visitor.identified) | Two-way: `try`, `send`, `do` |
| [Visitor.merge](/business/nouns/visitor/merge) | Merge joins two Visitor ids that belong to one person and keeps both as aliases. | [Analyst](/agents/marketing/analyst) | [`Visitor.merged`](/business/events/Visitor.merged) | Two-way: `try`, `send`, `do` |
| <a id="create"></a>[Visitor.create](/business/nouns/visitor/create) | Creates a Visitor. Inherited from [Noun](/business/nouns/noun). | None. | `Visitor.created` | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[Visitor.get](/business/nouns/visitor/get) | Returns one Visitor by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Visitor.list](/business/nouns/visitor/list) | Returns Visitors, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="update"></a>[Visitor.update](/business/nouns/visitor/update) | Changes the given properties of a Visitor. Inherited from [Noun](/business/nouns/noun). | None. | `Visitor.updated` | Two-way: `try`, `send`, `do` |
| <a id="delete"></a>[Visitor.delete](/business/nouns/visitor/delete) | Deletes a Visitor; its history is kept. Inherited from [Noun](/business/nouns/noun). | None. | `Visitor.deleted` | One-way: `send`, `do` |

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Visitor.create` | Yes | Yes | Yes | No |
| `Visitor.get` | Yes | Yes | Yes | Yes |
| `Visitor.list` | Yes | Yes | Yes | Yes |
| `Visitor.update` | Yes | Yes | Yes | No |
| `Visitor.delete` | Yes | Yes | Yes | No |
| `Visitor.forget` | Yes | No | No | No |
| `Visitor.identify` | Yes | Yes | Yes | No |
| `Visitor.merge` | Yes | Yes | Yes | No |

## Call it

**Ask**

```text
Identify the latest visitor with a contact.
```

**API**

```bash
curl -X POST https://api.sb/petpantry.shop/visitors/visitor_1/identify \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact":"contact_1"}'
```

```json
{
  "api": "https://api.sb/petpantry.shop",
  "contact": {
    "$id": "https://api.sb/petpantry.shop/contacts/contact_1",
    "$type": "Contact",
    "email": "lena@petpantry.shop",
    "phone": "Example phone"
  },
  "visitor": {
    "$id": "https://api.sb/petpantry.shop/visitors/visitor_1",
    "$type": "Visitor",
    "firstSeenAt": "2026-10-22T12:00:00Z"
  },
  "action": {
    "$id": "https://api.sb/petpantry.shop/actions/action_1",
    "operation": "Visitor.identify",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/petpantry.shop/actions/action_1"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const { contact, visitor } = await $.Visitor.identify({ visitor: 'visitor_1', contact: 'contact_1' })
```

**CLI**

```bash
npx startup-builder do Visitor.identify --json '{"visitor":"visitor_1","contact":"contact_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Visitor.identify({ visitor: 'visitor_1', contact: 'contact_1' })"
  }
}
```

## Example
Someone reads the pricing [Page](/business/nouns/page) of a meal-planning [App](/business/nouns/app) without signing in. The Visitor is known only as `v_8f2c`, an id no sibling Startup sees.