FEL API Usage Guide
This guide explains how to consume the RPAfelApi from other projects.
Table of Contents
- Base URL
- Authentication
- API Endpoints
- Request/Response Formats
- Code Examples
- Error Handling
- Deployment
Base URL
Production:
https://fel.rpapos.com/api/fel
Development:
https://fel-dev.rpapos.com/api/fel
Local Development:
http://localhost:5000/api/fel
Authentication
Most endpoints require an Authorization header with a token obtained from the certification provider (Digifact, Infile, etc.).
Get Token
Endpoint: POST /api/fel/GetToken
Request:
{
"UserName": "GT.000044653948.RPA_TEST",
"Password": "your-password"
}
Response:
{
"Success": true,
"response": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expira_en": "2024-01-01T12:00:00",
"otorgado_a": "GT.000044653948.RPA_TEST"
}
}
API Endpoints
1. Generate Certificate to Sign
Endpoint: POST /api/fel/generateCertificateToSign
Headers:
Authorization: Bearer token (required)Content-Type: application/json
Request Body: DTE JSON object (see DTE Structure)
Response (Success - 200):
{
"rpaUUID": "550e8400-e29b-41d4-a716-446655440000",
"Codigo": 0,
"Mensaje": "OK",
"AcuseReciboSAT": "A-1234567890",
"NITCertificador": "12345678",
"NombreCertificador": "DIGIFACT S.A.",
"Autorizacion_Serie": "A",
"Autorizacion_Numero": "123456",
"Autorizacion_Text": "A-123456",
"FechaHoraCertificacion": "2024-01-01T12:00:00Z"
}
Response (Error - 400):
{
"error": "rpaUUID invalido."
}
2. Void Certificate
Endpoint: POST /api/fel/voidCertificate
Headers:
Authorization: Bearer token (required)Content-Type: application/json
Request Body:
{
"NumeroDocumentoAAnular": "A-123456",
"NITEmisor": "44653948",
"IDReceptor": "49862952",
"FechaEmisionDocumentoAnular": "2024-01-01T12:00:00",
"FechaHoraAnulacion": "2024-01-01T13:00:00",
"MotivoAnulacion": "Error en facturación",
"Gface": "Digifact",
"rpaCertificador_Usuario": "GT.000044653948.RPA_TEST"
}
Response (Success - 200):
{
"ok": true,
"voidID": "firebase-document-id",
"data": {
"motivoAnulacion": "Error en facturación",
"fechaHoraAnulacion": "2024-01-01T13:00:00",
"numeroDocumentoAAnular": "A-123456",
"rpaUUID": "550e8400-e29b-41d4-a716-446655440000",
"serie": "A",
"autorizacion": "A-123456",
"numero": "123456"
}
}
3. Query Payer Info
Endpoint: POST /api/fel/QueryPayerInfo
Headers:
Authorization: Bearer token (optional, will auto-generate if not provided)Content-Type: application/json
Request Body:
{
"IdentificationType": "NIT",
"gface": "Digifact",
"payerId": "49862952",
"EmisorUser": "RPA_TEST",
"EmisorNIT": "44653948"
}
Response:
{
"Success": true,
"TaxPayerId": "49862952",
"TaxPayerName": "RANGEL,CASTRO,,KENNETH,ZAIDD",
"PersonalId": null
}
4. Send Email
Endpoint: POST /api/fel/mailFel
Headers:
Content-Type: application/json
Request Body:
{
"rpaUUID": "550e8400-e29b-41d4-a716-446655440000",
"emails": ["customer@example.com", "accounting@example.com"]
}
Response:
{
"id": "mail-queue-id"
}
DTE Structure
The DTE (Documento Tributario Electrónico) is the main document structure for electronic invoices in Guatemala.
Required Fields
{
"ID": "DatosCertificados",
"DatosEmision": {
"ID": "DatosEmision",
"DatosGenerales": {
"Tipo": "FACT",
"FechaHoraEmision": "2024-01-01T12:00:00",
"CodigoMoneda": "GTQ",
"rpaUUID": "550e8400-e29b-41d4-a716-446655440000",
"rpaDE_Empresa": "Digifact",
"rpaCertificador_Usuario": "GT.000044653948.RPA_TEST",
"rpaCertificador_Clave": "password",
"rpaFisco_Usuario": ""
},
"Emisor": {
"NITEmisor": "44653948",
"NombreEmisor": "COMPANY NAME, S.A.",
"CodigoEstablecimiento": "1",
"NombreComercial": "COMPANY",
"AfiliacionIVA": "GEN",
"DireccionEmisor": {
"Direccion": "ADDRESS",
"CodigoPostal": "01001",
"Municipio": "Guatemala",
"Departamento": "Guatemala",
"Pais": "GT"
}
},
"Receptor": {
"NombreReceptor": "CUSTOMER NAME",
"IDReceptor": "49862952",
"DireccionReceptor": {
"Direccion": "ADDRESS",
"CodigoPostal": "01001",
"Municipio": "Guatemala",
"Departamento": "Guatemala",
"Pais": "GT"
}
},
"Frases": {
"Frase": [
{
"TipoFrase": "1",
"CodigoEscenario": "1"
}
]
},
"Items": {
"Item": [
{
"NumeroLinea": "1",
"BienOServicio": "B",
"Cantidad": "1",
"UnidadMedida": "UNI",
"Descripcion": "Product description",
"PrecioUnitario": "100.00",
"Precio": "100.00",
"Descuento": "0",
"Total": "100.00",
"Impuestos": {
"Impuesto": [
{
"NombreCorto": "IVA",
"CodigoUnidadGravable": "1",
"MontoGravable": "89.29",
"MontoImpuesto": "10.71"
}
]
}
}
]
},
"Totales": {
"GranTotal": "100.00",
"TotalImpuestos": {
"TotalImpuesto": [
{
"NombreCorto": "IVA",
"TotalMontoImpuesto": "10.71"
}
]
}
}
},
"extra": {
"subDomain": "DMO1",
"TipoEspecial": "CUI",
"PropinaAdenda": "1"
}
}
Document Types (Tipo)
FACT- Factura (Invoice)NCRE- Nota de Crédito (Credit Note)NDEB- Nota de Débito (Debit Note)FESP- Factura EspecialFCAM- Factura CambiariaFCAP- Factura de CompraFPEQ- Factura Pequeño ContribuyenteNABN- Nota de AbonoRECI- ReciboRDON- Recibo de Donación
Certification Providers (rpaDE_Empresa)
Digifact- Digifact (default)Infile- InfileFegora- FegoraGuateFactura- GuateFactura
Code Examples
C# / .NET
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class FelApiClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public FelApiClient(string baseUrl = "https://fel.rpapos.com/api/fel")
{
_httpClient = new HttpClient();
_baseUrl = baseUrl;
}
public async Task<string> GetTokenAsync(string userName, string password)
{
var request = new
{
UserName = userName,
Password = password
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{_baseUrl}/GetToken", content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<TokenResponse>(responseJson);
return result.response.token;
}
public async Task<CertificateResponse> GenerateCertificateAsync(
object dteJson,
string token)
{
var json = JsonSerializer.Serialize(dteJson);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/generateCertificateToSign")
{
Content = content
};
request.Headers.Add("Authorization", token);
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<CertificateResponse>(responseJson);
}
}
JavaScript / TypeScript
class FelApiClient {
private baseUrl: string;
constructor(baseUrl: string = 'https://fel.rpapos.com/api/fel') {
this.baseUrl = baseUrl;
}
async getToken(userName: string, password: string): Promise<string> {
const response = await fetch(`${this.baseUrl}/GetToken`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ UserName: userName, Password: password }),
});
const data = await response.json();
if (!data.Success) {
throw new Error('Failed to get token');
}
return data.response.token;
}
async generateCertificate(dteJson: any, token: string): Promise<any> {
const response = await fetch(`${this.baseUrl}/generateCertificateToSign`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': token,
},
body: JSON.stringify(dteJson),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to generate certificate');
}
return await response.json();
}
async voidCertificate(request: any, token: string): Promise<any> {
const response = await fetch(`${this.baseUrl}/voidCertificate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': token,
},
body: JSON.stringify(request),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to void certificate');
}
return await response.json();
}
async queryPayerInfo(request: any, token?: string): Promise<any> {
const headers: any = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = token;
}
const response = await fetch(`${this.baseUrl}/QueryPayerInfo`, {
method: 'POST',
headers,
body: JSON.stringify(request),
});
return await response.json();
}
async sendEmail(rpaUUID: string, emails: string[]): Promise<any> {
const response = await fetch(`${this.baseUrl}/mailFel`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ rpaUUID, emails }),
});
return await response.json();
}
}
// Usage
const client = new FelApiClient();
const token = await client.getToken('GT.000044653948.RPA_TEST', 'password');
const certificate = await client.generateCertificate(dteJson, token);
Python
import requests
import json
class FelApiClient:
def __init__(self, base_url='https://fel.rpapos.com/api/fel'):
self.base_url = base_url
self.session = requests.Session()
def get_token(self, user_name, password):
url = f'{self.base_url}/GetToken'
payload = {
'UserName': user_name,
'Password': password
}
response = self.session.post(url, json=payload)
response.raise_for_status()
data = response.json()
if not data.get('Success'):
raise Exception('Failed to get token')
return data['response']['token']
def generate_certificate(self, dte_json, token):
url = f'{self.base_url}/generateCertificateToSign'
headers = {
'Authorization': token,
'Content-Type': 'application/json'
}
response = self.session.post(url, json=dte_json, headers=headers)
response.raise_for_status()
return response.json()
def void_certificate(self, request_data, token):
url = f'{self.base_url}/voidCertificate'
headers = {
'Authorization': token,
'Content-Type': 'application/json'
}
response = self.session.post(url, json=request_data, headers=headers)
response.raise_for_status()
return response.json()
def query_payer_info(self, request_data, token=None):
url = f'{self.base_url}/QueryPayerInfo'
headers = {'Content-Type': 'application/json'}
if token:
headers['Authorization'] = token
response = self.session.post(url, json=request_data, headers=headers)
return response.json()
def send_email(self, rpa_uuid, emails):
url = f'{self.base_url}/mailFel'
payload = {
'rpaUUID': rpa_uuid,
'emails': emails
}
response = self.session.post(url, json=payload)
response.raise_for_status()
return response.json()
# Usage
client = FelApiClient()
token = client.get_token('GT.000044653948.RPA_TEST', 'password')
certificate = client.generate_certificate(dte_json, token)
Swift (iOS)
import Foundation
class FelApiClient {
let baseURL: String
init(baseURL: String = "https://fel.rpapos.com/api/fel") {
self.baseURL = baseURL
}
func getToken(userName: String, password: String, completion: @escaping (String?, Error?) -> Void) {
let url = URL(string: "\(baseURL)/GetToken")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"UserName": userName,
"Password": password
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
completion(nil, error)
return
}
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let success = json["Success"] as? Bool,
success,
let response = json["response"] as? [String: Any],
let token = response["token"] as? String {
completion(token, nil)
} else {
completion(nil, NSError(domain: "FelApi", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to get token"]))
}
}.resume()
}
func generateCertificate(dteJson: [String: Any], token: String, completion: @escaping ([String: Any]?, Error?) -> Void) {
let url = URL(string: "\(baseURL)/generateCertificateToSign")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(token, forHTTPHeaderField: "Authorization")
request.httpBody = try? JSONSerialization.data(withJSONObject: dteJson)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
completion(nil, error)
return
}
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
completion(json, nil)
} else {
completion(nil, NSError(domain: "FelApi", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"]))
}
}.resume()
}
}
Java (Android)
import okhttp3.*;
import org.json.JSONObject;
import java.io.IOException;
public class FelApiClient {
private final String baseUrl;
private final OkHttpClient client;
public FelApiClient(String baseUrl) {
this.baseUrl = baseUrl;
this.client = new OkHttpClient();
}
public void getToken(String userName, String password, Callback callback) {
JSONObject body = new JSONObject();
try {
body.put("UserName", userName);
body.put("Password", password);
} catch (Exception e) {
callback.onFailure(null, new IOException(e));
return;
}
RequestBody requestBody = RequestBody.create(
body.toString(),
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url(baseUrl + "/GetToken")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.build();
client.newCall(request).enqueue(callback);
}
public void generateCertificate(JSONObject dteJson, String token, Callback callback) {
RequestBody requestBody = RequestBody.create(
dteJson.toString(),
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url(baseUrl + "/generateCertificateToSign")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", token)
.build();
client.newCall(request).enqueue(callback);
}
}
Error Handling
Common HTTP Status Codes
- 200 OK - Request successful
- 400 Bad Request - Invalid request data (missing rpaUUID, invalid DTE structure, etc.)
- 401 Unauthorized - Invalid or missing authorization token
- 406 Not Acceptable - Document already signed (will attempt to retrieve from provider)
- 500 Internal Server Error - Server error
Error Response Format
{
"error": "Error message description",
"code": 0,
"message": "Error message",
"description": "Detailed error description"
}
Deployment
Running Locally
- Navigate to the project directory:
cd csharp/FelApi/RPAfelApi
- Restore dependencies:
dotnet restore
- Run the application:
dotnet run
The API will be available at http://localhost:5000
Docker
Build the image:
docker build -t fel-api csharp/FelApi/RPAfelApi
Run the container:
docker run -d -p 8080:8080 -e ASPNETCORE_ENVIRONMENT=Production fel-api
Google Cloud Run
Deploy to Cloud Run:
gcloud builds submit --config cloudbuild.yaml --projectId barto-dev
Best Practices
- Always validate DTE structure before sending to the API
- Store tokens securely and refresh them before expiration
- Handle async responses - Some providers (Infile, Fegora) may return 406 and require polling
- Use retry logic for transient failures
- Log all requests for debugging and audit purposes
- Handle special cases:
- Special NITs that require specific phrases
- Tip handling via adenda
- CUI/EXT identification types
- Monitor Pub/Sub topics for success/failure events if you need async notifications
Support
For issues or questions, check:
- API documentation in
/docsfolder - Test examples in
/felApiTestfolder - XML examples in
/docs/FEL_Digifact/Ejemplos_XML_FER/