Authentication
The Owem Pay external API uses a three-layer security model: API Key + Secret, per-request HMAC-SHA512 signature, and mandatory IP whitelist.
Layer Overview
HTTP Request
|
+- 1. IP Whitelist --- Unauthorized IP? -> 403 Forbidden
|
+- 2. API Key + Secret --- Invalid credentials? -> 401 Unauthorized
|
+- 3. HMAC-SHA512 --- Invalid signature? -> 401 Unauthorized
|
+- Request accepted -> Business logicLayer 1 -- API Key + Secret
All requests must include the Authorization header:
Authorization: ApiKey {client_id}:{client_secret}| Component | Description | Prefix |
|---|---|---|
client_id | Public API Key identifier | cli_ |
client_secret | Secret key (we only store the hash) | sk_ |
The secret is never stored in plain text. When a request arrives, the submitted secret is compared against the stored hash. If it does not match, the request is rejected before reaching business logic.
Alternative format -- Basic Auth with base64:
Authorization: Basic {base64(client_id:client_secret)}Layer 2 -- HMAC-SHA512
Transactional requests (POST, PUT, PATCH) require an HMAC-SHA512 signature of the body in the hmac header. Validation uses constant-time comparison to prevent timing attacks.
See HMAC-SHA512 for implementation examples in 6 languages.
Layer 3 -- IP Whitelist
Every API Key must have at least one IP in the whitelist. Requests from unauthorized IPs are rejected with 403 Forbidden, even with valid credentials. Configure the whitelist in the Merchant Portal when creating or editing the API Key.
Supports individual IPs and CIDR notation (e.g., 172.20.16.0/20).
Required Headers
| Header | Value | Required |
|---|---|---|
Authorization | ApiKey {client_id}:{client_secret} | Yes -- all requests |
Content-Type | application/json | Yes -- requests with body |
hmac | HMAC-SHA512 signature in hexadecimal | Yes -- POST/PUT/PATCH only |
Idempotency-Key | Unique key for deduplication (max 256 chars) | Optional -- POST only |
Complete Example
CLIENT_ID="cli_a1b2c3d4e5f6"
CLIENT_SECRET="sk_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"
# Balance query (GET -- no HMAC)
curl -X GET https://api.owem.com.br/api/external/balance \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET"
# PIX Cash-Out (POST -- with HMAC + Idempotency-Key)
BODY='{"amount":3000,"pix_key":"12345678901","pix_key_type":"cpf","description":"Pagamento"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')
curl -X POST https://api.owem.com.br/api/external/pix/cash-out \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "hmac: $HMAC" \
-H "Idempotency-Key: cashout-order-9876" \
-d "$BODY"Additional Protections
| Protection | Description |
|---|---|
| Rate Limiting | 60,000 req/min per IP (authenticated). 5 req/min (unauthenticated) |
| Cloud Armor (WAF) | Web application firewall protecting the cluster |
| HTTPS + TLS 1.2+ | Mandatory encryption on all connections |
| HSTS | Browsers forced to use HTTPS |
Why HMAC-SHA512 and not mTLS?
mTLS (mutual TLS) authenticates the connection, not the content. If the connection is authenticated, all requests pass through without individual validation.
HMAC validates each request separately. Even within a valid connection, any change to the payload causes the request to be rejected.
| Aspect | mTLS | HMAC-SHA512 |
|---|---|---|
| Validates | TLS channel | Request payload |
| Management | X.509 certificates (issuance, rotation, revocation, CRL/OCSP) | Generate pair, update, invalidate |
| Operational risk | Expired certificates -- frequent cause of incidents | Key is a simple string |
| Content integrity | No | Yes |
TLS already ensures transport encryption. HMAC adds payload integrity and authenticity -- something that mTLS alone does not cover.
Error Responses
401 -- Missing Credentials
{
"error": {
"status": 401,
"message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
}
}403 -- Unauthorized IP
{
"error": {
"status": 403,
"message": "Request IP not in API key whitelist"
}
}429 -- Rate Limit Exceeded
{
"error": {
"status": 429,
"message": "Too Many Requests"
}
}Security
- Never expose the
client_secretin frontend code or public repositories - Use environment variables on your server
- The API Key is permanent -- it does not expire, but can be revoked in the Merchant Portal
- Configure allowed IPs in the whitelist