# Client API First Calls

https://dev.wisecp.com/es/client-api-first-calls

The five endpoints called while connecting to the client surface.

## Overview

The client API is for a customer managing their own account from an integration. It is a surface apart from the admin API: **a different address and a different kind of key**, with every call bounded to one customer.

This article answers the first three questions: is the surface up, what can my key do, and which values fill the address fields.

The address chain runs one way: the country code first, then the state number, then the city number. Each step wants the one before it, and any of them can come back empty.

## Reference

### The Health Check

get/api/v1/admin/client/ping

`System/Ping` no key needed

Returns that the client surface is up, along with the server time.

Response fields data — 3

pongboolWhether the surface is up.

versionstringThe API version.

timestringThe server's time. It comes in the server's own time zone.

Errors —

——This endpoint is open to everyone and returns no error.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/client/ping'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/client/ping');
const { data } = await res.json();

if (! data.pong) reportOutage();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/client/ping');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// This is SEPARATE from the admin surface's endpoint of the same name; one can be up while the other is not.
$up = Kernel::internal('client:System/Ping')['data']['pong'] ?? false;
```

### What the Key Is

get/api/v1/admin/client/whoami

`System/Whoami` no scope needed

Returns the key's identity, its permissions and the client it belongs to.

Response fields data — 6

idintThe key id.

typestringThe key kind. On this surface it is always a client key.

namestringThe name the key was given.

permissionsstring[]The scopes the key carries.

last_accessstringWhen it was last used.

owner_idintThe client the key belongs to. Every call is bounded by this client.

Errors 4

missing_token401The key was not sent or is not known.

key_revoked401The key was revoked.

audience_mismatch403An admin key was used on the client surface.

ip_not_allowed403The request came from outside the addresses allowed.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/client/whoami' \
  -H "Authorization: Bearer $CLIENT_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/client/whoami', {
  headers: { Authorization: `Bearer ${clientKey}` },
});

if (res.status === 403) return showWrongSurface();

const { data } = await res.json();
console.log(data.owner_id, data.permissions);
```

```php
$ch = curl_init('https://panel.example.com/api/v1/client/whoami');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// owner_id IS NOT PASSED on calls: the client comes from the key and writing it in the body changes nothing.
$me = Kernel::internal('client:System/Whoami', ['owner_id' => $ownerId])['data'];
```

### The Countries

get/api/v1/admin/client/reference/countries

`Reference/GetCountries` no scope needed

Returns the country codes the profile and address endpoints take.

Response fields data[] — 2

codestringThe two-letter country code. The profile and address endpoints want this rather than a number.

namestringThe country name. It comes in the site's language.

Errors 3

missing_token401The key was not sent or is not known.

key_revoked401The key was revoked.

audience_mismatch403An admin key was used on the client surface.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/client/reference/countries' \
  -H "Authorization: Bearer $CLIENT_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/client/reference/countries', {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
renderCountryPicker(data);
```

```php
$ch = curl_init('https://panel.example.com/api/v1/client/reference/countries');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The client surface speaks the COUNTRY CODE; the country NUMBER from the admin surface is not taken here.
$rows = Kernel::internal('client:Reference/GetCountries', ['owner_id' => $uid])['data'];
$codes = array_column($rows, 'code');
```

### A Country's States

get/api/v1/admin/client/reference/countries/{code}/states

`Reference/GetStates` no scope needed

Returns a country's states with the numbers the address fields use.

Response fields data[] — 2

idintThe state number. It is the state field on the address endpoints and the input to the city lookup.

namestringThe state name.

Errors 4

not_found404No such country code.

missing_token401The key was not sent or is not known.

key_revoked401The key was revoked.

audience_mismatch403An admin key was used on the client surface.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/client/reference/countries/TR/states' \
  -H "Authorization: Bearer $CLIENT_KEY"
```

```javascript
const res = await fetch(`https://panel.example.com/api/v1/client/reference/countries/${code}/states`, {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
if (! data.length) useFreeTextState();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/client/reference/countries/' . $code . '/states');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// AN EMPTY list is normal: with no states on that country the address field takes free text.
$states = Kernel::internal('client:Reference/GetStates', ['owner_id' => $uid, 'code' => $code])['data'];
$free   = ! $states;
```

### A State's Cities

get/api/v1/admin/client/reference/states/{id}/cities

`Reference/GetCities` no scope needed

Returns a state's cities with the numbers the address fields use.

Response fields data[] — 2

idintThe city number. It is the city field on the address endpoints.

namestringThe city name.

Errors 4

not_found404No such state.

missing_token401The key was not sent or is not known.

key_revoked401The key was revoked.

audience_mismatch403An admin key was used on the client surface.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/client/reference/states/34/cities' \
  -H "Authorization: Bearer $CLIENT_KEY"
```

```javascript
const res = await fetch(`https://panel.example.com/api/v1/client/reference/states/${stateId}/cities`, {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/client/reference/states/' . $stateId . '/cities');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// A CITY IS REQUIRED on an address even when the list is empty: write free text rather than leaving it out.
$cities = Kernel::internal('client:Reference/GetCities', ['owner_id' => $uid, 'id' => $stateId])['data'];
```

## Pitfalls

> **An admin key does not work on this surface**
> 
> The client surface takes a client key alone, and calling with an admin key gives `audience_mismatch`. That code means the **wrong surface** rather than a missing permission, so fixing the address instead of the key wastes time.

> **The customer comes from the key and never from the request**
> 
> Every client endpoint is bounded by the key's owner. Writing a customer number into the body **changes nothing**, and asking for someone else's record answers not found. Use the admin surface when you need to reach more than one customer.

> **The country speaks a code while the state and city speak numbers**
> 
> On the client surface a country is given as a **two-letter code** and the country number from the admin surface is not taken. The state and city want numbers. Filling all three the same way leads to a quiet validation error.

> **An empty list is not an error but a sign to use free text**
> 
> The state or city list can come back empty when the platform holds no data for that country or state. The address field then takes **free text**. A city stays required even with an empty list, and an address does not save without one.

> **The health check does not verify the key**
> 
> The health endpoint wants no credentials, so a **successful answer** says nothing about your key working. Call both while wiring an integration up: the health endpoint proves the server and the key endpoint proves the credentials.

## Related Articles

- [Account Details](https://dev.wisecp.com/en/the-account-of-the-key)
- [The Address Book](https://dev.wisecp.com/en/the-address-book)
