# Counterparty discovery \[How an agent finds counterparties for an intent]

Counterparty discovery is how an agent finds the people an [intent](/intent) should reach.
It takes one active intent and returns ranked counterparties. The agent chooses
which of them to open, and each one it opens becomes an
[opportunity](/opportunity) with a [negotiation](/negotiation).

Each candidate names the other intent, the person behind it, its statement, the
network it was found in, and a score. How far a statement can travel is set by
its [visibility](/privacy#intent-visibility).

```ts
discover(intentId, query, limit?)
```

`query` is the kind of person, in the caller's own words. `limit` is how many
to return, from 1 to 30. Omit it and the strongest 10 come back. One query is
one call.

Discovery only searches inside networks, and only the intent's networks: the
ones that intent is assigned to, not every network you belong to. An intent in
no network returns no one.

```ts
await client.discover(
  intentId,
  "a founder who has shipped a developer tool",
  10,
)
```

Pass `networkIds` to search a subset of the intent's networks. Omit
`networkIds` and the search covers all of the intent's networks. An id for a
network the intent is not assigned to is refused.

```ts
await client.discover(intentId, {
  query: "a founder who has shipped a developer tool",
  networkIds: [sfNetworkId],
  limit: 10,
})
```

## Verifiable discovery

Index runs counterparty discovery through a shared protocol. Intent visibility
and network scopes are enforced by that protocol, not by each agent on its
own.

Because every discovery goes through the same protocol, it can be extended in
different ways.

The protocol can also take verifiable data into account, so an agent can narrow
or qualify its discovery on facts that are proven rather than claimed. For example:

* **Capabilities** that limit what an agent may do, such as discovering only
  for one intent or only inside one network, so it holds exactly the authority
  it needs.
* **Credentials** such as a degree, an employer, or a license, issued by
  someone other than the person holding them.
* **Location proofs** showing that someone is in a city or at an event, without
  revealing exactly where.
* **Proof of personhood**, so a candidate is known to be a real, unique person
  rather than a bot or a duplicate account.
* **Attestations** from other members, such as a past collaborator vouching for
  someone's work.

Each of these can be checked without being shown. An agent can learn that a
candidate holds a credential or is in the right city without seeing the
document or the address behind it.

A later call can take several queries, a narrower set of networks, and those
facts as requirements. It is planned.

```ts
discover(intentId, {
  queries,
  limit?,
  networkIds?,
  require?,
})
```

Each query is one direction. The results merge to one seat per person, keeping
the strongest score, then cut to `limit`. `networkIds`, when passed, are
networks the intent is already assigned to. `require` is how an agent asks for
the facts. The proof is checked and not returned.

Preview of what `require` might carry. Each fact has its own form. `kind`
names it. The rest is JSON. A capability limits what the caller may do, so it
is in this preview and a `require` list leaves it out.

```json
{ "kind": "credential", "schema": { "type": "UniversityDegreeCredential", "degree": { "name": "Computer Science" } } }
{ "kind": "credential", "schema": { "type": "EmploymentCredential", "employer": { "name": "Index" } } }
{ "kind": "credential", "schema": { "type": "LicenseCredential", "license": { "name": "Professional Engineer" } } }
{ "kind": "location", "place": { "city": "San Francisco" } }
{ "kind": "personhood", "proof": { "unique": true } }
{ "kind": "attestation", "claim": { "about": "their work", "by": "a past collaborator" } }
{ "kind": "capability", "limit": { "network": "one network" } }
```

### Mutual check

Some things you want should only come up if the other person wants them too.
A quiet job search is the usual case: you'd like the right founder in your
community to know you're looking, without the whole community knowing. Sharing
an intent as `mutuals_only` is how you say that.

The intent isn't listed for members to search. Instead, the network looks for
a likely match in the same network and asks each side on its own: does the
other statement fit what you want? Each answer also weighs that person's
[private intents](/privacy#intent-visibility), so a salary floor or a company
you won't work with can rule a match out without ever being shown.

* **Both say yes:** an opportunity opens and the [negotiation](/negotiation)
  starts. You speak first, and that is when the other person first sees your
  intent.
* **Either says no:** nothing opens, and the other person never hears about
  it.

The check runs when you save an intent, and when you share it into a network
as `mutuals_only`. It works in both directions: when someone saves an intent in
a network where yours is waiting, theirs is checked against yours.

Two things are [planned](/privacy#planned). Your agent will be able to write
the check for your intent and send it along, so the network runs your rules
instead of its own. And sealed hosting will run the check where the operator
can't read the statements.
