Documentação de Integração
API de Licenciamento EngiCheck — Guia completo para desenvolvedores
Endpoint base
https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGatewayVisão Geral
A API de Licenciamento EngiCheck é o único ponto de contato entre o seu aplicativo e o sistema de controle de acesso da plataforma. Ela valida usuários, gerencia sessões simultâneas e aplica os limites de cada plano contratado.
Autenticação segura
Toda requisição precisa do x-api-key no header.
Seats simultâneos
A API controla quantos usuários podem estar ativos ao mesmo tempo.
Stateful
Sessões ficam ativas por 2h sem ping. Após isso são expiradas.
Autenticação
Todas as requisições precisam incluir a chave de API no header x-api-key. Sem essa chave, a API retorna 401 Unauthorized.
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action": "ping", "session_token": "abc..."}'🔐 Segurança
Nunca exponha a LICENSE_API_KEY no código do cliente. Armazene em variáveis de ambiente ou no keychain do dispositivo. Em caso de comprometimento, solicite uma nova chave via suporte.
Fluxo de Integração
validateValidar acesso ao abrir o appVerifique se o usuário já tem acesso aprovado e a licença está ativa. Se válido → pule para o passo 3.
request_accessSolicitar acesso (se não aprovado)Cria solicitação pendente. Mostre tela de 'aguardando aprovação' e tente validar novamente depois.
loginCriar sessão (login)Cria uma sessão ativa e retorna o session_token. Armazene-o localmente.
pingHeartbeat a cada 15 minutosRenova a sessão. Sem ping por 2h a sessão expira e o usuário é deslogado automaticamente.
logoutLogout ao fechar o appLibera o seat imediatamente para outro usuário poder entrar.
Endpoints
Todos os endpoints usam POST no mesmo URL base. O campo action no body define a operação.
Cria uma solicitação de acesso para um usuário. O admin do portal EngiCheck precisa aprovar antes que o usuário possa fazer login. Se já existe solicitação pendente ou aprovada, retorna o status correspondente sem criar duplicata.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "request_access" |
| string | Sim | E-mail do usuário que quer acesso | |
| tenant_email | string | Sim | E-mail da empresa (tenant) — usado para vincular à licença |
| user_name | string | Não | Nome do usuário (recomendado para o admin identificar) |
| company_name | string | Não | Nome da empresa |
| app | string | Não | Identificador do app. Default: "engcheck" |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "request_access",
"email": "joao@construtora.com",
"tenant_email": "admin@construtora.com",
"user_name": "João Silva",
"company_name": "Construtora ABC",
"app": "engcheck"
}'Resposta de sucesso (200)
{
"success": true,
"status": "pending",
"message": "Solicitação enviada. Aguarde aprovação."
}Possíveis erros
company_no_license— Empresa não possui licença ativa→ Mostrar tela de contato para contratar plano
license_expired— Licença vencida→ Avisar para renovar
request_pending— Solicitação já enviada e aguardando aprovaçãoalready_approved— Usuário já tem acesso aprovado → ir para loginVerifica se o usuário tem acesso aprovado e se a licença da empresa está ativa e dentro da validade. Não cria sessão. Use ao abrir o app para decidir o próximo passo.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "validate" |
| string | Sim | E-mail do usuário | |
| tenant_email | string | Sim | E-mail da empresa (tenant) |
| app | string | Não | Identificador do app. Default: "engcheck" |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "validate",
"email": "joao@construtora.com",
"tenant_email": "admin@construtora.com",
"app": "engcheck"
}'Resposta de sucesso (200)
{
"valid": true,
"license": {
"id": "lic_abc123",
"plan": "profissional",
"expiry_date": "2026-12-31",
"seat_limit": 15
}
}Possíveis erros
access_not_approved— Admin ainda não aprovou o acesso→ Mostrar tela de aguardando aprovação
company_no_license— Empresa sem licença ativalicense_expired— Licença vencida — valid retorna falseCria uma sessão ativa para o usuário e retorna o session_token. Respeita o limite de seats simultâneos da licença. Se o usuário já tem uma sessão ativa, a reutiliza. Sessões expiradas de outros usuários são automaticamente limpas.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "login" |
| string | Sim | E-mail do usuário | |
| tenant_email | string | Sim | E-mail da empresa (tenant) |
| device_info | string | Não | Ex: 'iPhone 14 / iOS 17.2' — para rastreabilidade |
| ip_address | string | Não | IP do dispositivo |
| app | string | Não | Identificador do app. Default: "engcheck" |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "login",
"email": "joao@construtora.com",
"tenant_email": "admin@construtora.com",
"device_info": "Samsung Galaxy S23 / Android 14",
"ip_address": "189.40.12.55",
"app": "engcheck"
}'Resposta de sucesso (200)
{
"success": true,
"session_token": "a1b2c3d4e5f6...",
"license": {
"plan": "profissional",
"expiry_date": "2026-12-31",
"seat_limit": 15,
"app": "engcheck"
},
"active_seats": 3,
"seat_limit": 15
}Possíveis erros
access_not_approved— Usuário não tem acesso aprovadocompany_no_license— Empresa sem licença ativalicense_expired— Licença vencidaseat_limit_reached— Todos os seats simultâneos em uso→ Avisar para aguardar ou fazer upgrade de plano
⚠ Observações
- Armazene o session_token localmente (SharedPreferences, SecureStorage, etc.).
- Se o usuário já tem sessão ativa, a API a reutiliza e retorna reused_session: true.
- Sessões expiradas de outros usuários são limpas automaticamente no login.
Renova a sessão ativa (heartbeat). Deve ser chamado a cada 15 minutos enquanto o app estiver em uso. Sem ping por 2 horas, a sessão é automaticamente marcada como expirada e o seat liberado.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "ping" |
| session_token | string | Sim | Token retornado no login |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "ping",
"session_token": "a1b2c3d4e5f6..."
}'Resposta de sucesso (200)
{
"valid": true
}Possíveis erros
session_expired— Sessão expirada (sem ping por 2h)→ Redirecionar para tela de login
⚠ Observações
- Recomendamos um timer de 15 minutos para chamar o ping em background.
- Se valid retornar false, o usuário deve fazer login novamente.
Encerra a sessão do usuário e libera imediatamente o seat para outro usuário. Chame sempre ao fechar o app ou ao fazer logout manual.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "logout" |
| session_token | string | Sim | Token da sessão ativa |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "logout",
"session_token": "a1b2c3d4e5f6..."
}'Resposta de sucesso (200)
{
"success": true
}⚠ Observações
- Mesmo que o token já esteja inválido, a API retorna success: true.
- Chame no onDispose ou lifecycleObserver do app para garantir a liberação do seat.
Retorna quantos usuários simultâneos a licença permite, quantos estão ativos agora e quantos slots ainda estão disponíveis. Útil para exibir no painel administrativo do cliente.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "check_seats" |
| tenant_email | string | Sim | E-mail da empresa (tenant) |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "check_seats",
"tenant_email": "admin@construtora.com"
}'Resposta de sucesso (200)
{
"seat_limit": 5,
"active_seats": 2,
"available_seats": 3,
"plan": "essencial",
"status": "ativa"
}Possíveis erros
company_no_license— Empresa sem licença ativaRetorna o número máximo de obras ativas permitidas pelo plano da licença. Use para bloquear a criação de novas obras quando o limite for atingido.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "check_obras" |
| tenant_email | string | Sim | E-mail da empresa (tenant) |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "check_obras",
"tenant_email": "admin@construtora.com"
}'Resposta de sucesso (200)
{
"obras_limit": 3,
"plan": "essencial",
"status": "ativa",
"expiry_date": "2026-12-31"
}Possíveis erros
company_no_license— Empresa sem licença ativa⚠ Observações
- 9999 significa ilimitado (plano Consultor).
- Esta API não rastreia obras criadas — apenas informa o limite do plano. O controle de quantas obras existem é responsabilidade do seu app.
Lista todas as sessões ativas de uma empresa. Use para painel administrativo — não expor para usuários finais.
Parâmetros do Body (JSON)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| action | string | Sim | "sessions" |
| tenant_email | string | Sim | E-mail da empresa (tenant) |
Exemplo de requisição (cURL)
curl -X POST https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway \
-H "x-api-key: SUA_LICENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "sessions",
"tenant_email": "admin@construtora.com"
}'Resposta de sucesso (200)
{
"sessions": [
{
"user_email": "joao@construtora.com",
"device_info": "Samsung Galaxy S23",
"last_ping": "2026-04-08T14:30:00Z",
"status": "active"
}
],
"count": 1,
"seat_limit": 5
}Limites por Plano
| Plano | Usuários simultâneos | Obras ativas |
|---|---|---|
| Start | 2 | 1 |
| Essencial | 5 | 3 |
| Profissional | 15 | 5 |
| Consultor | Ilimitado | Ilimitado |
seat_limit retornado no login e no check_seats reflete o plano contratado. O admin pode ter um limite customizado configurado diretamente na licença — neste caso prevalece sobre o padrão do plano.Tabela de Códigos de Erro
| Código (reason) | Descrição | Ação recomendada no app |
|---|---|---|
access_not_approved | Admin não aprovou ainda | Mostrar tela 'Aguardando aprovação' |
company_no_license | Empresa sem licença ativa | Redirecionar para contratar plano |
license_expired | Licença vencida | Mostrar aviso de renovação |
seat_limit_reached | Todos os seats em uso | Avisar para aguardar ou fazer upgrade |
request_pending | Solicitação já enviada e pendente | Mostrar 'Aguardando aprovação' |
already_approved | Acesso já aprovado | Redirecionar para login |
session_expired | Sessão expirou (2h sem ping) | Redirecionar para login |
Exemplo em Flutter/Dart
Exemplo completo de como integrar o fluxo de licenciamento em um app Flutter.
import 'dart:convert';
import 'package:http/http.dart' as http;
const String API_URL =
"https://api.base44.app/api/apps/68120a83c0d84e8b4d62c82b/functions/licenseGateway";
const String API_KEY = "SUA_LICENSE_API_KEY"; // Guarde no .env ou no keychain
class LicenseService {
static final _headers = {
'Content-Type': 'application/json',
'x-api-key': API_KEY,
};
static Future<Map<String, dynamic>> _post(Map body) async {
final res = await http.post(
Uri.parse(API_URL),
headers: _headers,
body: jsonEncode(body),
);
return jsonDecode(res.body);
}
/// 1. Validar acesso ao abrir o app
static Future<Map<String, dynamic>> validate(String email, String tenantEmail) {
return _post({
'action': 'validate',
'email': email,
'tenant_email': tenantEmail,
'app': 'engcheck',
});
}
/// 2. Solicitar acesso (quando validate retorna access_not_approved)
static Future<Map<String, dynamic>> requestAccess(
String email, String tenantEmail, String name, String company) {
return _post({
'action': 'request_access',
'email': email,
'tenant_email': tenantEmail,
'user_name': name,
'company_name': company,
'app': 'engcheck',
});
}
/// 3. Login — cria sessão e retorna session_token
static Future<Map<String, dynamic>> login(String email, String tenantEmail) {
return _post({
'action': 'login',
'email': email,
'tenant_email': tenantEmail,
'device_info': 'Flutter App',
'app': 'engcheck',
});
}
/// 4. Ping — heartbeat a cada 15min
static Future<void> ping(String sessionToken) async {
await _post({'action': 'ping', 'session_token': sessionToken});
}
/// 5. Logout ao fechar o app
static Future<void> logout(String sessionToken) async {
await _post({'action': 'logout', 'session_token': sessionToken});
}
/// Checar limite de obras
static Future<Map<String, dynamic>> checkObras(String tenantEmail) {
return _post({'action': 'check_obras', 'tenant_email': tenantEmail});
}
}
// ─── Uso no app ───────────────────────────────────────────────────────────────
void main() async {
const email = 'joao@construtora.com';
const tenant = 'admin@construtora.com';
// 1. Validar
final v = await LicenseService.validate(email, tenant);
if (v['valid'] == true) {
// 3. Login direto
final login = await LicenseService.login(email, tenant);
if (login['success'] == true) {
final token = login['session_token'];
print('Logado! Token: $token');
// 4. Ping periódico (a cada 15min)
// Timer.periodic(Duration(minutes: 15), (_) => LicenseService.ping(token));
// 5. Logout ao fechar
// await LicenseService.logout(token);
}
} else if (v['reason'] == 'access_not_approved') {
// 2. Solicitar acesso
await LicenseService.requestAccess(email, tenant, 'João', 'Construtora ABC');
print('Solicitação enviada!');
}
}
EngiCheck API · v1.0 · Qualquer dúvida entre em contato com o suporte técnico via WhatsApp