> ## 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.

# URL patterns

> The pattern grammar for reference kinds, with allowed and rejected examples, inferred parameters and the error messages TypeScript shows.

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

A pattern tells Reintersect which URLs belong to a reference kind. Patterns use a small subset of
the [URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) syntax: an `https`
scheme, a host and a path. The grammar is narrow on purpose. Every pasted link in a workspace is
checked against every installed app's patterns, so a pattern must be cheap to match and must not
claim links that belong to someone else.

## Writing a pattern

```ts theme={null}
defineReference("issue", {
  title: "Issues",
  patterns: { issue: "https://linear.app/:workspace/issue/:id/:slug?" },
  resolve: async ({ params }) => ({
    externalId: params.id,
    title: params.id,
    summary: `Linear issue ${params.id} in ${params.workspace}.`,
  }),
});
```

`patterns` maps a pattern id of your choice to a pattern string. `params` is typed from the string:
in this example it is `{ workspace: string; id: string; slug?: string }`, and a typo such as
`params.idd` is a type error.

Patterns must be string literals. A pattern built at runtime from a variable is rejected with
`Reference patterns must be string literals so Reintersect can match them statically`.

## The grammar

| Part | Allowed | Notes |
| - | - | - |
| Scheme | `https` | No other scheme. |
| Credentials and port | None | `user:pass@` and `:8080` are rejected. |
| Host | A literal host, `*.` followed by a registrable domain, or `{*.}?` followed by a literal host | `*.acme.com` matches subdomains but not `acme.com` itself. `{*.}?acme.com` matches both. |
| Path segment | A literal, or one whole `:name` parameter | Names use letters, digits and `_`. |
| Optional segment | `:name?`, final segment only | Matches with or without the last segment. |
| Trailing slash | `{/}?` at the very end | Without it, a URL that ends in `/` doesn't match. |
| Wildcard | One `*`, as the final segment | Captured as `rest`. |
| Search and hash | None | References match scheme, host and path only. |

## Allowed examples

| Pattern | Matches | `params` |
| - | - | - |
| `https://vercel.com/:team/:project/:deploymentId` | `https://vercel.com/acme/web/dpl_9xK2` | `{ team, project, deploymentId }` |
| `https://linear.app/:workspace/issue/:id/:slug?` | `https://linear.app/acme/issue/ENG-12` and `https://linear.app/acme/issue/ENG-12/fix-login` | `{ workspace, id, slug? }` |
| `https://*.example.com/docs/*` | `https://help.example.com/docs/billing/invoices` | `{ rest }` |
| `https://{*.}?status.example.com/incidents/:id` | `https://status.example.com/incidents/42` and `https://eu.status.example.com/incidents/42` | `{ id }` |
| `https://*.acme.vercel.app/:id` | `https://preview-12.acme.vercel.app/report` | `{ id }` |
| `https://example.com/runs/:runId{/}?` | `https://example.com/runs/812` and `https://example.com/runs/812/` | `{ runId }` |

## Rejected examples

TypeScript checks the grammar when you write the pattern, and Reintersect checks it again when you
deploy. The type error is the message in the table.

| Pattern | Message |
| - | - |
| `http://example.com/:id` | `Reference patterns must use https:// (found "http://")` |
| `example.com/:id` | `Reference patterns must be absolute https:// URLs` |
| `https://example.com:8443/:id` | `Host "example.com:8443" has a port or :param; hosts must be literal or a leading "*." wildcard` |
| `https://*.com/:id` | `Wildcard "*.com" spans a public suffix: use a registrable domain such as "*.example.com"` |
| `https://*.vercel.app/:id` | `Wildcard "*.vercel.app" spans a public suffix: use a registrable domain such as "*.example.vercel.app"` |
| `https://*.*.example.com/:id` | `Host "*.*.example.com" may contain only one leading "*." wildcard` |
| `https://app-*.example.com/:id` | `Host "app-*.example.com" may only use a leading "*." wildcard label` |
| `https://github.com/:owner/:repo/pull/:number(\d+)` | `Custom regex groups are not supported in ":number(\d+)": use a plain :param and validate it in resolve` |
| `https://example.com/:a-:b` | `Segment ":a-:b" has more than one :param: each path segment may hold at most one` |
| `https://example.com/v:version` | `Parameters must occupy a whole path segment (found "v:version")` |
| `https://example.com/:org?/settings` | `Only the final path segment may be optional (":org?")` |
| `https://example.com/*/edit` | `"*" is only allowed as the final path segment` |
| `https://example.com/files/*.pdf` | `"*" must be a whole path segment (found "*.pdf")` |
| `https://example.com/:file*` | `Segment ":file*" mixes a :param with "*"` |
| `https://example.com/:project-id` | `Parameter name "project-id" must use only letters, digits and _` |
| `https://example.com/search?q=:query` | `Search patterns (?...) are not supported: references match scheme, host and path only` |
| `https://example.com/docs#:anchor` | `Hash patterns (#...) are not supported: references match scheme, host and path only` |

The public suffix check uses the full [Public Suffix List](https://publicsuffix.org/), including
shared hosting suffixes such as `vercel.app`, `github.io` and `pages.dev`. A wildcard must sit under a
domain one organization controls.

## Parameters

* `:name` captures one path segment as a string.
* `:name?` captures the final segment when present; the field is optional in `params`.
* A final `*` captures the rest of the path, including slashes, as `params.rest`.
* Values stay percent-encoded. `https://example.com/files/Q3%20plan` gives `{ name: "Q3%20plan" }`
  for the pattern `https://example.com/files/:name`. Decode with `decodeURIComponent` when you need
  the text.
* Parameters are untrusted input. Validate their format in `resolve`, for example with a Zod
  schema, before you use them in a request.

## Several URL shapes for one kind

When the same object has more than one URL shape, give each shape its own entry in `patterns`.
`resolve` receives the id of the pattern that matched as `patternId`, and `params` is narrowed
accordingly:

```ts theme={null}
defineReference("deployment", {
  title: "Deployments",
  patterns: {
    dashboard: "https://vercel.com/:team/:project/:deploymentId",
    preview: "https://*.acme.vercel.app/:path?",
  },
  resolve: async (context) => {
    const deployment =
      context.patternId === "dashboard"
        ? await getDeployment(context.params.deploymentId)
        : await getDeploymentByHost(context.url.host);

    return {
      externalId: deployment.id,
      title: deployment.name,
      summary: `Deployment ${deployment.name}, ${deployment.state}.`,
    };
  },
});
```

Return the same `externalId` from both shapes so both links show the same shared object. Pattern ids
are 1 to 64 characters from `A-Z`, `a-z`, `0-9`, `_`, `.` and `-`.

## How matching works

Reintersect matches URLs on its own servers. Browsers, the editor and your app never run the
patterns. When a URL matches, Reintersect stores which pattern matched and the extracted parameters,
and the SDK routes the call to your `resolve` by pattern name. Your runtime doesn't need URLPattern
support.

When more than one pattern matches a URL, from your app or from different apps, Reintersect picks
the most specific one:

1. The pattern with the most literal characters wins.
2. On a tie, a pattern without a trailing catch-all (`/*`) wins over one with it, so
   `https://example.com/:team/:project` beats `https://example.com/*`.
3. On a further tie, the pattern with fewer wildcards and parameters wins.
4. On a final tie, the member's own development installs come first, then workspace installs in the
   order they were installed.

## Claimed domains

Every host your patterns use should fall under a domain listed in the app's `domains` option.
`reintersect check` warns about a pattern whose host isn't claimed:

```ts theme={null}
createReintersectApp({
  name: "Launchpad",
  slug: "launchpad",
  domains: ["vercel.com", "acme.vercel.app"],
});
```

Apps installable by link match a pattern only once its host is under a verified domain. Private apps
in their own workspace, and development installs, skip verification. See
[Domain verification](/references/domain-verification).

## Limits

| Limit | Value |
| - | - |
| Patterns per reference kind | 50 |
| Path segments per pattern | 16 |
| Parameters per pattern | 8 |
| Length of a URL Reintersect will match | 2 KB |


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