# Partner API

> Create and manage stores, act inside them, manage their staff and save their PayPoint keys with a partner secret key.

Source: https://docs.nexafone.so/v1/partners/api/

The Partner API is for the partner’s **servers**: it takes a partner secret key (`psk_live_…`/`psk_test_…`) as
`Authorization: Bearer` and refuses browsers (no CORS). The full reference is [Partner API](/v1/api/partner/); the
Node, PHP, Python and Go [helpers](/v1/server/) include a client.

## Stores

Create a store when a business is created on your platform. The call is **idempotent on `externalRef`**: repeating it
returns the same store, so retries are safe.

```ts title="partner.ts"
// When a store opens on your platform: idempotent on externalRef, so retrying returns the same store.
export async function openStore(shop: {
  id: string
  name: string
  ownerName: string
  ownerEmail: string
}) {
  const store = await nexafon.stores.create({
    externalRef: shop.id,
    name: shop.name,
    owner: { name: shop.ownerName, email: shop.ownerEmail },
    country: 'SO',
    timezone: 'Africa/Mogadishu',
    locale: 'so',
  })
  return store.workspaceId
}
```

```bash
curl -X POST "https://api.nexafone.so/api/v1/partner/stores" \
  -H "Authorization: Bearer $NEXAFON_PARTNER_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"externalRef": "shop-1", "name": "Shop One", "owner": {"name": "Amina Ali", "email": "amina@shop.example"}, "country": "SO", "timezone": "Africa/Mogadishu", "locale": "so"}'
```

The new store starts on your default store plan with its subscription running, and answers with its `workspaceId`.
Then:

| Operation | |
| --- | --- |
| `GET /api/v1/partner/stores` | Your stores, paged |
| `GET /api/v1/partner/stores/{externalRef}` | One store |
| `PATCH /api/v1/partner/stores/{externalRef}` | Rename it, or reopen a closed store with `status: active` |
| `POST /api/v1/partner/stores/{externalRef}/close` | Close it: it becomes read-only and keeps its data |
| `GET /api/v1/partner` | Your partner profile, status and default plan’s features |

## Acting inside a store

The same key calls the workspace operations of the [API](/v1/api/) for **your own** stores, by `workspaceId`:

```bash
curl "https://api.nexafone.so/api/v1/workspaces/$WORKSPACE_ID/conversations" \
  -H "Authorization: Bearer $NEXAFON_PARTNER_SECRET_KEY"
```

A workspace that is not one of your stores (another partner’s, or a business outside your platform) answers `404`,
exactly like one that does not exist. The key’s workspace scopes limit what it may do there.

## Staff

Staff sign in with [partner user tokens](/v1/partners/user-tokens/), which create or update their membership of each
store they list. Leaving a store out of a token does **not** remove access; tell Nexafon explicitly:

```ts title="partner.ts"
// Staff are managed on your platform: change a role or remove someone from a store when they change there.
export async function staffChanged(storeRef: string, userId: string, role: string | null) {
  if (role === null) await nexafon.members.disable(storeRef, userId)
  else await nexafon.members.update(storeRef, userId, { role })
}
```

| Operation | |
| --- | --- |
| `PUT /api/v1/partner/stores/{externalRef}/members/{partnerUserId}` | Change the user’s role (and teams) in that store; their menu updates live |
| `DELETE /api/v1/partner/stores/{externalRef}/members/{partnerUserId}` | Disable them in that store; their open sessions end within seconds |

## The store’s PayPoint key

Store sales are paid into the store’s own PayPoint account (see [store payments](/v1/commerce/payments/)):

| Operation | |
| --- | --- |
| `PUT /api/v1/workspaces/{workspaceId}/payments/paypoint` | Save the store’s API key and merchant id; answers only “saved” and the last 4 characters |
| `POST …/payments/paypoint/test` | Check the key with PayPoint without charging anyone |
| `PUT …/payments/paypoint/wallets` | The wallets customers may pay with |
| `DELETE …/payments/paypoint` | Remove the key |

The key is encrypted at rest and never returned or logged.

## Retries and idempotency

Creating operations accept an `Idempotency-Key` header; see the [API overview](/v1/api/#idempotency). Rate limits are
per key ([rate limits](/v1/rate-limits/)).
