# Make your first API call (/api/app/first-call)

The App API is called from your server with an access token from Poltura ID: your app's own (the JWT profile
with its machine user) or a person's through your app. Every call names the store installation in
`Poltura-Installation`. See [Auth](/apps/auth) for both.

`PolturaClient` from `@poltura/app-sdk` sends the token and the installation to `/apps-api/v1`. Read the store,
then walk the product list page by page until `nextCursor` is `null`:

```ts title="server.ts"
async function listAllProducts(client: PolturaClient): Promise<string[]> {
  const titles: string[] = [];
  let cursor: string | undefined;
  do {
    const page = await client.products.list({ limit: 100, cursor });
    titles.push(...page.products.map((product) => product.title));
    cursor = page.nextCursor ?? undefined;
  } while (cursor !== undefined);
  return titles;
}
```

## What to know next [#what-to-know-next]

- **Errors** come back as `PolturaRequestError` with a `code`, the HTTP `status` and a `requestId` to quote
  when you ask for help. `rate_limited` carries `retryAfterMs`. The full list is on [App API](/api/app#errors).
- **Writes** take an `Idempotency-Key`, so a retried request is applied once. Storage writes also take the
  `expectedVersion` they are based on.
- **Webhooks** tell you when products or orders change, so you do not have to poll. See
  [Webhooks](/apps/webhooks).

- [App API reference](/api/app): Every route, permission and error.
- [Authentication](/apps/auth): Tokens, permissions and session tokens.
- [List products](/api/app/products/list): Parameters and response of the call above.
