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

# Read the directory from code

> List the ODP services in the InFlow directory with the public API or the ODP SDKs. Then read the catalog of each service.

Copy the list of ODP services in the InFlow directory into your own platform with its public API. You need no InFlow account, no login, and no API key.

The directory gives you the list of services, not their catalogs. To read the offerings of a service, connect to that service with the ODP agent SDK.

## List the services

Call the service search endpoint without a search phrase.

```sh theme={null}
curl https://api.inflowpay.ai/v1/services/search \
  -H 'Content-Type: application/json' \
  --data '{"limit":100}'
```

The response contains the published, visible services that have a valid ODP document.

| Field | Content |
| - | - |
| `items` | The service metadata. This includes `service_origin`, the name, the description, the advertised operations and protocols, and `indexed_at`. |
| `next` | A continuation URL. It is present only when more results are available. |
| `facets` | Search summaries. You do not need them to copy the list. |

### Get every page

* The default page size is 50. The maximum is 100.
* Send a GET request to the `next` URL. Continue until the response has no `next` field.
* A relative `next` URL resolves against `https://api.inflowpay.ai`.
* Treat the `next` URL as opaque. Do not build it or change its cursor.

## List the services with the Node SDK

```sh theme={null}
npm install @offering-protocol/directory
```

```typescript theme={null}
import { createDirectoryClient } from "@offering-protocol/directory";

const directory = createDirectoryClient();
const services = [];

for await (const page of directory.searchServices({ limit: 100 }).pages) {
  if (page.issues?.length) {
    throw new Error(
      `Directory entries could not be read: ${JSON.stringify(page.issues)}`
    );
  }

  services.push(...page.items);
}

// Replace the local snapshot only after the complete traversal succeeds.
console.log(JSON.stringify(services, null, 2));
```

The client follows the continuation links. It reads `https://directory.offeringprotocol.org`, and you cannot change this URL. For the sandbox, use `createDirectoryClient({ environment: "sandbox" })`.

<Warning>
  Always check `page.issues`. The client puts malformed entries there and leaves them out of `items`, without failing the page. Without this check, you can lose entries and not know it.
</Warning>

### Fields that the SDK leaves out

The directory SDK leaves out these service document fields, because its parser does not validate them.

`branding`, `mcp`, `http`, `odp_version`, `payment_origins`, `search_capabilities`

To get one of these fields, call the API directly, or read the live Service Document of the service.

### SDKs for other languages

Each ODP SDK includes directory support. The method names and the iteration APIs differ by language. Each repository documents its directory client.

| Language | Repository |
| - | - |
| Go | `offering-protocol/odp-go` |
| Java | `offering-protocol/odp-java` |
| Node.js and TypeScript | `offering-protocol/odp-node` |
| Python | `offering-protocol/odp-python` |
| Rust | `offering-protocol/odp-rust` |

## Keep a local copy in sync

For a small directory, use this approach.

<Steps>
  <Step title="Get the full list on a schedule">
    Read every page of the list at a regular interval.
  </Step>

  <Step title="Key each entry by its origin">
    Store or update each entry with `service_origin` as the key.
  </Step>

  <Step title="Replace the old copy only after a full read">
    Replace the previous copy only when every page loads with no rejected entries. Only then remove the entries that are missing from the new list.
  </Step>

  <Step title="Keep the old copy on failure">
    If a request fails, keep the previous copy.
  </Step>
</Steps>

The list is a paginated discovery listing, not a transactional database snapshot or an incremental change feed. You do not need a separate bulk export.

## Read the catalog of a service

The directory entry is cached discovery metadata. To read the current catalog, connect to the service at its `service_origin` with the ODP agent SDK.

```sh theme={null}
npm install @offering-protocol/agent
```

```typescript theme={null}
import { createOdpServiceClient } from "@offering-protocol/agent";

// Use service.service_origin from a Directory result.
const odp = createOdpServiceClient({
  serviceUrl: "https://demo.inflowpay.ai"
});

const inspection = await odp.inspect();

// The validated Service Document includes advertised branding, MCP endpoints,
// HTTP configuration, and search capabilities when the Service supplies them.
console.log(JSON.stringify(inspection.document, null, 2));

const operations = inspection.capabilities.operations;
const supports = (name: string) =>
  operations.some((operation) => operation.name === name);

if (supports("list-offerings") && supports("get-offering")) {
  // The client follows the Service's pagination links automatically.
  for await (const summary of odp.listOfferings().items) {
    const offering = await odp.getOffering(summary.id);
    console.log(JSON.stringify(offering, null, 2));
  }
}
```

| Call | What it does |
| - | - |
| `inspect()` | Gets and validates the `/.well-known/odp` document of the service. |
| `listOfferings()` | Returns short catalog entries. The client follows the pagination links. |
| `getOffering(id)` | Returns the full details: descriptions, images, prices, and actions, when the service gives them. It also resolves the attribute schema of the offering and validates its attributes. |

If `getOffering` cannot resolve the attributes, read the returned `issues`. Do not assume that the offering has no attributes.

### Use only the advertised operations

A service without `list-offerings` and `get-offering` does not have an empty catalog. Some services use search or collections instead. Check the advertised operations. Then use `searchOfferings()`, `listCollections()`, or `listCollectionOfferings(collectionId)` as the service supports.

### Catalogs can need authentication or payment

The directory API is public, but the catalog operations of a service can need authentication or payment. Before you read a catalog, check the `authentication` value of each operation and the advertised payment protocols. Authenticated or paid access needs its own code.

<Warning>
  A protected catalog or an unavailable catalog is not an empty catalog. Never record a service as having no offerings because a catalog call failed.
</Warning>

These calls only read data. They do not invoke the actions of an offering or buy anything.

The agent SDK also supports authentication, pagination, caching, and action resolution. See the `packages/agent` folder in `offering-protocol/odp-node`.

## Related pages

<CardGroup cols={2}>
  <Card title="The InFlow directory" icon="folder-open" href="/concepts/directory">
    Read what a listing shows, and search the directory with the CLI.
  </Card>

  <Card title="Discovery with ODP" icon="compass" href="/concepts/discovery">
    Read how services, collections, offerings, and actions fit together.
  </Card>
</CardGroup>


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