# Request signing (HMAC)

Every API v2 request is an HTTP `POST` with
`Content-Type: application/x-www-form-urlencoded` and exactly four fields:

| Field | Value |
|---|---|
| `app_id` | Your application ID from the Apaczka panel |
| `request` | The method payload, **JSON-serialized to a string** |
| `expires` | Unix timestamp until which the request is valid (max +15 minutes) |
| `signature` | HMAC-SHA256 signature — see below |

## The signature

The string to sign is four values joined with `:`

```
app_id : route : request : expires
```

where `route` is the method path **including path parameters and the
trailing slash** — e.g. `order_send/`, `points/INPOST/`, or
`waybill/123456/`. The signature is the lowercase-hex HMAC-SHA256 of that
string, keyed with your `app_secret`.

## Worked example (PHP)

```php
$appId     = 'your-app-id';
$appSecret = 'your-app-secret';
$route     = 'order_send/';                 // trailing slash is required
$payload   = json_encode($order);           // the method payload
$expires   = time() + 900;                  // now + 15 minutes

$stringToSign = sprintf('%s:%s:%s:%s', $appId, $route, $payload, $expires);
$signature    = hash_hmac('sha256', $stringToSign, $appSecret);

$response = $http->post('https://www.apaczka.pl/api/v2/' . $route, [
    'form_params' => [
        'app_id'    => $appId,
        'request'   => $payload,
        'expires'   => $expires,
        'signature' => $signature,
    ],
]);
```

:::tip

Sign **exactly the string you send** as `request`. Re-serializing the
payload after signing (different key order, whitespace, escaping) is the
most common cause of `Invalid signature` errors.

:::

## Common pitfalls

- **Missing trailing slash in `route`** — `order_send` signs differently
  than `order_send/`. The server signs with the trailing slash.
- **Path parameters belong in the route** — for `waybill/123456/` you sign
  the full route including the order ID.
- **`expires` too far in the future** — the limit is 15 minutes.
- **HTTP 200 does not mean success** — see
  [Error semantics](/legacy/error-semantics).

Full method-by-method reference:
[API v2](/reference/v2-legacy).
