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

# Resolving references

> Turn a matched URL into core fields, an access result, or a custom card, and choose who may see it.

> Reintersect apps are in developer preview. Names and limits can still change before general
> release.

`resolve` turns a matched URL into a description of the object behind it. Reintersect calls it when a
URL first appears, when a member views a restricted reference whose answer for them is out of date,
and when you [ask for a refresh](/references/updates).

## Context

```ts theme={null}
defineReference("deployment", {
  title: "Deployments",
  patterns: { dashboard: "https://vercel.com/:team/:project/:deploymentId" },
  config: z.object({ team: z.string() }),
  env: { VERCEL_TOKEN: secret() },
  resolve: async ({ params, url, patternId, reintersect, env, config }) => {
    // …
  },
});
```

| Field | Type | Description |
| - | - | - |
| `params` | Inferred from the pattern | Parameters captured from the URL, still percent-encoded. See [URL patterns](/references/patterns#parameters). |
| `patternId` | Your pattern ids | Which pattern matched. Narrows `params` when you check it. |
| `url` | `URL` | The URL as the member pasted it. |
| `actor` | `{ memberId, name }` | The member the URL is resolved for, when there is one. |
| `reintersect` | Scoped client | A Reintersect API client limited to the scopes this reference declares. |
| `env`, `config` | Typed | The environment and install config this reference declares. |

`search` receives `query` and `limit` instead of `params`, `patternId` and `url`.

## Core fields

Return an object with the core fields. Reintersect builds the chip, link title, hover card, default
card and agent text from them.

```ts theme={null}
resolve: async ({ params, env }) => {
  const deployment = await getDeployment(params.deploymentId, env.VERCEL_TOKEN.reveal());

  return {
    externalId: deployment.id,
    title: `${params.project} ${deployment.meta.branch}`,
    subtitle: `Deployed by ${deployment.creator.username}`,
    icon: "rocket",
    status: { state: deployment.ready ? "success" : "running", label: deployment.state },
    updatedAt: deployment.updatedAt,
    summary: `Deployment of ${params.project} from branch ${deployment.meta.branch}. State: ${deployment.state}.`,
  };
},
```

| Field | Type | Required | Description |
| - | - | - | - |
| `externalId` | `string` | Yes | Your stable ID for the object, 1 to 200 characters. Reintersect keeps one shared record per kind and `externalId` in each workspace. |
| `title` | `string` | Yes | Shown on the chip, as the link title and as the card heading. At most 200 characters. |
| `summary` | `string` | No | One or two plain sentences describing the object's current state, at most 280 characters. Agents and hover cards read it. Defaults to `title`. |
| `subtitle` | `string` | No | A secondary line on hover cards and the default card, such as an owner or project. |
| `icon` | `IconName` | No | An icon from the [allowed list](/components/overview#icons). Defaults to your app icon. |
| `status` | `{ state, label }` | No | `state` is a [status state](/components/data#status) such as `"running"`; `label` is the text members see, at most 60 characters. |
| `updatedAt` | `string \| Date` | No | When the object last changed. Shown as a relative time. |
| `visibility` | `"public" \| "restricted"` | No | Defaults to `"restricted"`. See [Visibility](#visibility). |
| `block` | `{ props, state? }` | No | Props and state for a custom card. Requires the kind's `block` option. See [Custom cards](#custom-cards). |

Write the `summary` for someone who hasn't opened the link. "Deployment of web from main, ready
since 14:02" tells an agent and a hovering member more than "Vercel deployment".

## Access results

When the object doesn't exist or can't be shown, return an access result instead of the core
fields:

```ts theme={null}
resolve: async ({ params, env }) => {
  const response = await fetch(`https://api.vercel.com/v13/deployments/${params.deploymentId}`, {
    headers: { authorization: `Bearer ${env.VERCEL_TOKEN.reveal()}` },
  });

  if (response.status === 404) return { access: "notFound" };
  if (response.status === 403) return { access: "forbidden" };
  if (response.status === 401) return { access: "unauthorized" };

  const deployment = await response.json();
  // …
},
```

| Result | Meaning | What the member sees |
| - | - | - |
| `{ access: "notFound" }` | The object doesn't exist or was deleted. | A plain link chip marked "Not found". |
| `{ access: "forbidden" }` | The object exists, but this member or install may not read it. | A plain link chip marked "Restricted". |
| `{ access: "unauthorized" }` | Your app isn't connected or its credentials expired. | A plain link chip marked "Not connected", with a prompt to check the connection in workspace settings (or to ask an admin). |

A withheld answer never replaces fields another member was granted. Agents receive only the URL and
the access result.

If `resolve` throws, the SDK logs the error and answers `unavailable` for that URL: Reintersect keeps
the link as it is, caches nothing, and tries again later. The other URLs in the batch are unaffected.
A timeout of the whole batch behaves the same way.

## Visibility

`visibility` decides whether the shared record is shown to everyone or checked per member:

| Visibility | Behaviour |
| - | - |
| `"restricted"` (default) | Reintersect remembers, per member, whether their own resolve was granted. Members with a granted answer see the full reference; members whose answer was withheld see only the plain link chip. Each member's answer is fresh for 30 minutes; after that, Reintersect resolves again when they view the reference. Restricted references are also left out of memory extraction and shared agent context. |
| `"public"` | Every member of the workspace sees the shared fields from any granted answer. |

Use `actor` in `resolve` to decide whether the member may see the object in your system, and return
`{ access: "forbidden" }` when they may not. Return `"public"` only for objects everyone in the
workspace is allowed to see, because a public answer is shown to all of them.

Share pages and connected workspaces never call your app; they show the stored record read-only.

## Custom cards

The default card shows the core fields. To show more, such as a deployment's checks or a ticket's
assignees, give the kind a `block` and return that block's props, and optionally its state:

```tsx theme={null}
import { defineBlock, defineReference } from "@reintersect/sdk/app";
import { Block, Fact, Facts, Header, Status } from "@reintersect/sdk/components";
import { z } from "zod";

export const DeploymentCard = defineBlock({
  type: "deployment-card",
  title: "Deployment",
  props: z.object({ project: z.string(), branch: z.string(), state: z.string(), url: z.string() }),
  state: z.object({}),
  initialState: {},
}).render((props) => (
  <Block summary={`${props.project}: ${props.state}`}>
    <Header href={props.url} trailing={<Status state="success" label={props.state} />}>
      {props.project}
    </Header>
    <Facts>
      <Fact label="Branch">{props.branch}</Fact>
    </Facts>
  </Block>
));

export const DeploymentRef = defineReference("deployment", {
  title: "Deployments",
  patterns: { dashboard: "https://vercel.com/:team/:project/:deploymentId" },
  block: DeploymentCard,
  resolve: async ({ params }) => {
    const deployment = await getDeployment(params.deploymentId);

    return {
      externalId: deployment.id,
      title: `${params.project} deployment`,
      block: {
        props: {
          project: params.project,
          branch: deployment.meta.branch,
          state: deployment.state,
          url: deployment.inspectorUrl,
        },
      },
    };
  },
});
```

The props and state you return are checked against the block's schemas; a wrong field is a type
error on `resolve`. The SDK renders the block during the resolve and Reintersect stores the tree with
the record. The card uses the same components, limits and read-only modes as any other block, and
replaces only the card body: the chip, link title, hover card and agent text still come from the
core fields.

Leave `insertable` off (its default) on blocks that only exist as reference cards, so they don't
appear in the slash menu or as agent insert tools. The app must still include the block with
`.addBlock(DeploymentCard)`; TypeScript reports it as missing otherwise.

## Timing and batching

Resolving is an interactive call: Reintersect waits up to 5 seconds for your answer and doesn't
retry. When several of your links arrive at once, for example in a pasted list, one delivery carries
up to 20 URLs. The SDK calls your `resolve` for them concurrently and returns the answers together.
If you implement the [delivery protocol](/reference/delivery-protocol) without the SDK, answer every
item in the batch within the deadline.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.