Quickstart in 5 minutes
This is the whole path, from zero to the first response.
Before you start
Section titled “Before you start”- The company needs a plan that includes the API, from Start on.
- You need to be OWNER or ADMIN of it. Only those two roles create keys.
- Have your panel password at hand: the creation form asks for it.
1. Create the key
Section titled “1. Create the key”In the panel, open Settings, go to the API tab and click New key.
| Field | What to fill in |
|---|---|
| Name | To recognise later which system uses this key, such as “Store ERP” |
| What this key can do | Tick only what the integration uses. See Permissions |
| Expiration | Never expires, 30 days, 90 days or 1 year |
| Allowed IPs | Optional. One per line, address or range. Empty accepts any IP |
| Password | Your panel password |
Copy the key and store it in a secret manager or in an environment variable of your server. Never in code, never in a repository, never in the browser.
2. Store the key in the environment
Section titled “2. Store the key in the environment”export FH_API_KEY="fh_live_your_key"The examples below read the key from that variable. That way it is not written in the file you commit.
3. Make the first call
Section titled “3. Make the first call”GET /public/v1/me answers whose key this is. It is the right call to confirm that everything is in place before you write the rest of the integration.
curl https://api.fatureihoje.com/public/v1/me \ -H "Authorization: Bearer $FH_API_KEY" \ -H "Accept-Language: en"const res = await fetch('https://api.fatureihoje.com/public/v1/me', { headers: { Authorization: `Bearer ${process.env.FH_API_KEY}`, 'Accept-Language': 'en', },});
const body = await res.json();
if (!res.ok) { throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);}
console.log(body.organization.name, body.key.permissions);Run it with node me.mjs. Node.js 18 or newer, which already ships fetch.
<?php
$ch = curl_init('https://api.fatureihoje.com/public/v1/me');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('FH_API_KEY'), 'Accept-Language: en', ],]);
$raw = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$body = json_decode($raw, true);
if ($status !== 200) { throw new RuntimeException($status . ' ' . $body['error']['code'] . ': ' . $body['error']['message']);}
echo $body['organization']['name'], PHP_EOL;Run it with php me.php. It needs the curl and json extensions, which are enabled in most installations.
import jsonimport osimport urllib.errorimport urllib.request
request = urllib.request.Request( "https://api.fatureihoje.com/public/v1/me", headers={ "Authorization": f"Bearer {os.environ['FH_API_KEY']}", "Accept-Language": "en", },)
try: with urllib.request.urlopen(request) as response: body = json.load(response)except urllib.error.HTTPError as failure: error = json.load(failure)["error"] raise SystemExit(f"{failure.code} {error['code']}: {error['message']}")
print(body["organization"]["name"], body["key"]["permissions"])Run it with python3 me.py. Standard library only, nothing to install.
4. The response
Section titled “4. The response”{ "object": "api_key_context", "organization": { "id": "0f3a5f1e-9f7a-4f2b-8f4c-2a1d9e6b7c30", "name": "Marcenaria Souza", "timezone": "America/Sao_Paulo", "currency": "BRL" }, "key": { "id": "6d2c8b41-77a3-4a5e-9c10-3b8f0d5e41aa", "name": "Store ERP", "prefix": "fh_live_7Qx4Kd", "permissions": ["clients:read", "leads:create"], "expires_at": null }, "acting_as": { "member_id": "b1d4e2f0-5c69-4a31-9d77-8e2c4f6a0b53", "name": "Ana Souza", "role": "owner" }, "limits": { "requests_per_minute": 60 }}| Field | What it is |
|---|---|
organization | The company that owns the key, with the time zone and the currency it uses |
key.prefix | The start of the key, the same the panel shows in the list. It tells you which key is in use |
key.permissions | What this key can do, in the module:action format |
key.expires_at | Expiration date in ISO 8601, or null when the key does not expire |
acting_as | The company member the key acts as, and their role |
limits.requests_per_minute | Calls per minute for the company, adding up all keys. null means no ceiling |
The Reference lists these fields one by one, with type and format.
5. When you get a 401
Section titled “5. When you get a 401”{ "error": { "type": "authentication_error", "code": "api_key_invalid", "message": "Invalid, revoked or expired API key.", "request_id": "0a8c1f2e-3b4d-4c5f-9a6b-7c8d9e0f1a2b", "doc_url": "https://docs.fatureihoje.com/en/errors#api_key_invalid" }}This is the answer to any authentication refusal, always the same. It does not say the reason, on purpose. Check in this order:
- The header is
Authorization: Bearer <key>, with a single space betweenBearerand the key. - The key was copied whole, with no space or line break at the end.
- The key is neither revoked nor expired. The panel shows the state of each key.
- If the key has an IP allowlist, your server’s outbound IP is on it.
- The address is
api.fatureihoje.com. The panel host does not answer/public/v1.
Next, open View usage in the key menu, in the panel. It shows the calls of the last 30 days with date, method, route, status, duration, IP and request_id, refused ones included. That is where you see the IP your server really uses.
Keep the request_id from the response. It is how support finds the call.
Next step
Section titled “Next step”- Authentication: key format, IP allowlist, rotation and revocation.
- Permissions: why a key with the permission can still get a 403.