# Discovery

Your `OrderInventory` service answers on `orders.inventory.check`, and `ShippingQuote` answers on `shipping.quote`. To call them, you had to know those subjects in advance. This page removes that requirement.

Every service the framework creates also answers a second, automatic set of subjects: the **discovery verbs**. Through them, a caller can ask the running system what services exist, what each one answers, and how it's doing, without anyone hard-coding a subject or maintaining a registry on the side.

You add no code to get this. The moment `AddService` returned on the `your-first-service` page, the framework subscribed to these subjects for you. This page shows how to use them.

## The three discovery verbs

Discovery is **learning what services exist and what they answer** through a fixed set of subjects under the `$SRV` prefix. Every service answers three verbs there:

* **PING**: checks whether a service is present. The reply carries the service name, instance id, and version. Use it to find services and measure round-trip time.
* **INFO**: reports what a service answers. The reply adds the description and the list of endpoints, each with its subject and queue group.
* **STATS**: reports how a service is doing. The reply adds per-endpoint counters. We read those on the next page; for now, it's enough to know the verb exists.

The verbs are uppercase, and so are their subjects. A service doesn't publish to `$SRV` itself; it subscribes there and replies to your requests, the same request-reply you already know.

## Three levels of address

Each verb answers at three levels, narrowing from every service down to one instance:

| Subject                         | Who answers                           |
| ------------------------------- | ------------------------------------- |
| `$SRV.INFO`                     | every service on the connection       |
| `$SRV.INFO.OrderInventory`      | every instance named `OrderInventory` |
| `$SRV.INFO.OrderInventory.<id>` | the one instance with that id         |

The same three levels work for `PING` and `STATS`. The first level is how you enumerate an unfamiliar system. The second narrows to one service name. The third reaches a single **instance**: one running copy of a service, identified by the **service id** the framework generated for it.

Ask `OrderInventory` to describe itself at the name level, and read the endpoint list out of the reply:

#### CLI

```
#!/bin/bash

# Ask the OrderInventory service to describe itself. INFO is a discovery

# verb every service answers automatically on the $SRV subject tree. The

# name level ($SRV.INFO.OrderInventory) reaches every running instance of

# OrderInventory, and each one replies with its name, id, version, and the

# list of endpoints it serves.

#

# Discovery is broadcast: a plain request would return only the first

# reply, so the CLI waits a short window and collects every responder.

# That is why the timeout matters here.

nats request '$SRV.INFO.OrderInventory' '' --replies=0 --timeout=1s



# The reply for each instance is JSON like:

#

#   {

#     "type": "io.nats.micro.v1.info_response",

#     "name": "OrderInventory",

#     "id": "JJTBR2MN3JSAYBI5ST7E32",

#     "version": "1.0.0",

#     "endpoints": [

#       { "name": "check",

#         "subject": "orders.inventory.check",

#         "queue_group": "q" }

#     ]

#   }

#

# Read the endpoints array to learn what the service answers and on which

# subject -- no documentation lookup required.

#

# The same information, formatted, comes from the CLI ops shortcut:

nats service info OrderInventory
```

The reply tells you the `check` endpoint listens on `orders.inventory.check` in queue group `"q"`. That's the same fact you configured by hand earlier, but now any caller can read it back from the live service instead of trusting documentation.

The `$SRV.PING`/`INFO`/`STATS` wire format and JSON response schemas are documented in [Reference](/reference/.md). We only need the behavior here.

## Discovery is broadcast, not load-balanced

One detail on this page works differently than you might expect. The endpoints you built answer in a **queue group**, so each request goes to exactly one instance. The discovery verbs do **not**. A `$SRV.INFO.OrderInventory` request reaches *every* instance named `OrderInventory`, and every one of them replies.

That's deliberate. The point of discovery is to get a response from every instance, all three of them, not whichever one happened to answer first. So a caller doesn't wait for a single reply; it waits a short deadline and collects however many responses arrive in that window.

**Message flow — serviceDiscoveryAnimated:** Interactive NATS flow diagram.

* client → server
* server → client

The animation shows it: one `$SRV.INFO.OrderInventory` request fans out to all three instances, and three INFO replies come back. The single targeted query at the end, `$SRV.STATS.OrderInventory.id2`, reaches only `id2`, because the third address level pins the request to one instance.

## Targeting one instance

When you already know an id (from a PING or INFO reply), you can address that instance alone. This is how you inspect or compare a single copy of a service that's running many.

The third level reaches exactly one instance, so you expect a single reply and don't need a deadline loop:

#### CLI

```
#!/bin/bash

# The third discovery level adds the service id, addressing exactly one

# instance instead of all of them. Use it when several instances of

# OrderInventory are running and you want to inspect one in particular.

#

# Pick an id from an earlier INFO or PING reply, then target it. Replace

# the id below with one your own service printed.

INSTANCE_ID="JJTBR2MN3JSAYBI5ST7E32"



# $SRV.STATS.OrderInventory.<id> reaches only the instance with that id,

# so a single reply is expected -- no deadline loop needed for one

# instance.

nats request "\$SRV.STATS.OrderInventory.$INSTANCE_ID" '' --timeout=1s



# PING at the instance level is the lightweight check: it confirms that

# one specific instance is alive and reports its name, id, and version.

nats request "\$SRV.PING.OrderInventory.$INSTANCE_ID" '' --timeout=1s
```

## Collecting replies with the CLI

For interactive use, the `nats` CLI wraps the broadcast loop. `nats service list` enumerates every service and instance it can find; `nats service info` formats one service's endpoints; `nats service ping` measures round-trip time to each responder:

```
nats service list

nats service info OrderInventory

nats service ping
```

These run the same `$SRV` requests internally and gather the replies by deadline. They're the fastest way to see what's running while you develop; the programmatic verbs above are what your own tooling uses.

## Pitfalls

Two mistakes are common the first time you query discovery. Both come from the broadcast behavior above.

**Discovery is broadcast: a single reply is not the whole answer.** A plain request returns the first response and stops. Against `$SRV.INFO.OrderInventory` that gives you one instance and silently hides the rest, so five running instances appear as one. Don't treat a discovery request like a normal request-reply call. Wait a deadline and collect every reply, exactly as the CLI does:

#### CLI

```
#!/bin/bash

# Ask the OrderInventory service to describe itself. INFO is a discovery

# verb every service answers automatically on the $SRV subject tree. The

# name level ($SRV.INFO.OrderInventory) reaches every running instance of

# OrderInventory, and each one replies with its name, id, version, and the

# list of endpoints it serves.

#

# Discovery is broadcast: a plain request would return only the first

# reply, so the CLI waits a short window and collects every responder.

# That is why the timeout matters here.

nats request '$SRV.INFO.OrderInventory' '' --replies=0 --timeout=1s



# The reply for each instance is JSON like:

#

#   {

#     "type": "io.nats.micro.v1.info_response",

#     "name": "OrderInventory",

#     "id": "JJTBR2MN3JSAYBI5ST7E32",

#     "version": "1.0.0",

#     "endpoints": [

#       { "name": "check",

#         "subject": "orders.inventory.check",

#         "queue_group": "q" }

#     ]

#   }

#

# Read the endpoints array to learn what the service answers and on which

# subject -- no documentation lookup required.

#

# The same information, formatted, comes from the CLI ops shortcut:

nats service info OrderInventory
```

The reverse holds too: when you want one specific instance, use the `$SRV.STATS.OrderInventory.<id>` level instead of filtering a broadcast. It reaches that instance directly and returns one reply.

**`$SRV` is a reserved subject prefix: do not publish to it yourself.** The framework owns the entire `$SRV` tree; services subscribe there to answer PING, INFO, and STATS. Publishing your own messages under `$SRV` collides with that machinery and corrupts what callers discover. Keep your own subjects under your own prefixes (`orders.*`, `shipping.*`) and let the framework own `$SRV`. Who may even see `$SRV` across account boundaries is a separate question, covered in [Security](/learn/security/.md).

## Where you are

You can now discover the system instead of memorizing it. With `$SRV` PING, INFO, and STATS, you can enumerate every service on the connection, ask one service which endpoints it answers, and target a single instance by its id. You also know the catch: discovery is broadcast, so you gather replies by deadline rather than taking the first one.

`OrderInventory` and `ShippingQuote` are still running. Nothing about them changed; you just learned to ask them what they are.

## What's next

INFO told you *what* a service answers. The STATS verb tells you *how it's doing*: request counts, error counts, and processing time per endpoint. The next page reads those counters and shows how a handler records an error.

Continue to [Observability](/learn/services/observability.md).

## See also

* [Request-reply](/learn/core-nats/request-reply.md) — the mechanism every `$SRV` reply rides on.
* [Observability](/learn/services/observability.md) — what the STATS verb reports and how to read it.
* [Reference](/reference/.md) — the `$SRV` wire format and response schemas.
