Quickstart: your first Argentine invoice by API
This guide takes a developer or an AI agent from zero to an electronic invoice with a CAE (the ARCA authorization code), then to webhooks and production. The base URL is https://api.yo-facturo.com/api/v1.
Two ways in
Pick one. The REST API with a token (yf_test_* or yf_live_*) gives full control, including invoicing, and is available on the Premium plan and during the free trial. The MCP server gives conversational access from Claude, ChatGPT or Cursor with an OAuth sign-in and no token to copy; it is available from the Basic plan. The last section covers MCP.
1. Get a sandbox key
In the panel go to Settings, Integrations, API Tokens and create a token in test mode. A yf_test_* token works on an isolated sandbox database and never touches real data. The token is shown once, so store it right away.
For this guide the token needs the scopes invoices:write, invoices:read, webhooks:write and webhooks:read, plus accounting:write if you want to switch the ARCA environment. Send the key in either header; X-API-Key wins if both are present.
X-API-Key: yf_test_xxxxxxxx
Authorization: Bearer yf_test_xxxxxxxx
Limits are 10 requests per second, 100 per minute, 3,000 per hour and 20,000 per day for the whole account, shared by all tokens. Invoice creation and authorization are further limited to 30 per 5 minutes.
2. Issue an invoice in the sandbox
With a test key, POST /bill/receipts/invoice/ never calls ARCA. It stores a simulated invoice with simulated: true, livemode: false and a synthetic CAE, which is what you want while building the integration. Prices in items are final, IVA included.
Always send an Idempotency-Key. Repeating the same key and body replays the first response and adds the header Idempotency-Replayed: true; the same key with a different body returns 422.
curl -s -X POST "https://api.yo-facturo.com/api/v1/bill/receipts/invoice/" \
-H "X-API-Key: $YF_TEST_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"punto_venta": 1,
"concepto": 1,
"tipo_comprobante": 6,
"tipo_documento": 99,
"numero_documento": "0",
"razon_social": "Consumidor Final",
"condicion_iva_receptor_id": 5,
"items": [{"descripcion": "Test item", "cantidad": 1, "precio_unitario": 1000}]
}'
The response has the shape {"success": true, "data": {"id": "...", "cae": "...", "simulated": true, "livemode": false}}. Read data.cae. The same call in Python:
import os, uuid, requests
BASE = "https://api.yo-facturo.com/api/v1"
H = {"X-API-Key": os.environ["YF_TEST_KEY"]}
body = {
"punto_venta": 1, "concepto": 1, "tipo_comprobante": 6,
"tipo_documento": 99, "numero_documento": "0",
"razon_social": "Consumidor Final", "condicion_iva_receptor_id": 5,
"items": [{"descripcion": "Test item", "cantidad": 1, "precio_unitario": 1000}],
}
r = requests.post(f"{BASE}/bill/receipts/invoice/", json=body,
headers={**H, "Idempotency-Key": str(uuid.uuid4())}, timeout=30)
r.raise_for_status()
invoice = r.json()["data"]
print(invoice["id"], invoice.get("cae"), invoice.get("simulated"))
And in Node.js 18 or later:
const BASE = "https://api.yo-facturo.com/api/v1";
const res = await fetch(`${BASE}/bill/receipts/invoice/`, {
method: "POST",
headers: {
"X-API-Key": process.env.YF_TEST_KEY,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
punto_venta: 1, concepto: 1, tipo_comprobante: 6,
tipo_documento: 99, numero_documento: "0",
razon_social: "Consumidor Final", condicion_iva_receptor_id: 5,
items: [{ descripcion: "Test item", cantidad: 1, precio_unitario: 1000 }],
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { data } = await res.json();
console.log(data.id, data.cae, data.simulated);
You can wipe the sandbox at any time with POST /sandbox/reset/. It only works with a test key; a live key gets 403.
3. Get a real homologation CAE from ARCA
The sandbox CAE is synthetic. To get a CAE issued by ARCA's homologation web services, use a live key on an account switched to the testing environment. In testing you do not need your own certificate or delegation: the platform's homologation CUIT and certificate are used for you.
# 1) switch the account to ARCA homologation (scope accounting:write)
curl -s -X POST "https://api.yo-facturo.com/api/v1/bill/tax_info/afip-environment/" \
-H "X-API-Key: $YF_LIVE_KEY" -H "Content-Type: application/json" \
-d '{"environment": "testing"}'
# 2) create the invoice as a draft. "ambiente" is read from every request body
# and defaults to "production": always send "testing" while you test.
curl -s -X POST "https://api.yo-facturo.com/api/v1/bill/receipts/invoice/" \
-H "X-API-Key: $YF_LIVE_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"ambiente": "testing", "autorizar": false, "punto_venta": 1, "concepto": 1,
"tipo_comprobante": 6, "tipo_documento": 99, "numero_documento": "0",
"razon_social": "Consumidor Final", "condicion_iva_receptor_id": 5,
"items": [{"descripcion": "Homologation test", "cantidad": 1, "precio_unitario": 1000}]}'
# 3) authorize it: synchronous, returns the CAE or ARCA's rejection reason in a 400
curl -s -X POST "https://api.yo-facturo.com/api/v1/bill/receipts/authorize/<id>/" \
-H "X-API-Key: $YF_LIVE_KEY" -H "Content-Type: application/json" \
-d '{"ambiente": "testing"}'
The data.cae of the authorize response is the homologation CAE. Why two steps: with "autorizar": true in step 2 the authorization runs in the background and the response carries authorization_pending: true with no CAE yet, so you would have to poll GET /bill/receipts/{id}/. The two-step flow is synchronous and surfaces the rejection reason, which is what an agent needs to correct itself.
One gotcha: the invoice number is reserved at creation, not at authorization. If ARCA rejects a draft, later invoices can fail with ARCA error 10016 until you run POST /bill/receipts/doctor/ with {"apply": true}.
4. Subscribe to a webhook
Register an endpoint with the scope webhooks:write. The signing secret is returned once.
curl -s -X POST "https://api.yo-facturo.com/api/v1/webhooks/endpoints" \
-H "X-API-Key: $YF_LIVE_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/yf-webhook", "events": ["invoice.authorized"], "description": "agent"}'
# -> 201 {"data": {"id": "...", "secret": "whsec_...", "events": [...], "mode": "live"}}
Deliveries are POST requests with the headers X-YF-Signature, X-YF-Event and X-YF-Delivery. Verify the signature before trusting the body; the webhooks guide has the format, the verification code and the retry schedule. Reply with a 2xx within 10 seconds.
5. Go live
- Delegate the invoicing service. Production needs the taxpayer to delegate the electronic invoicing service (wsfe) to the platform CUIT in ARCA's portal; the Spanish guide Delegar el web service de facturación walks through it. Confirm it with
POST /bill/tax_info/verify-delegation/and the body{"cuit": "..."}. - Switch the environment back with
POST /bill/tax_info/afip-environment/and{"environment": "production"}. Accounts flagged as testing by an admin cannot do this themselves. - Drop
"ambiente": "testing"from your requests, since the default is production, and use ayf_live_*key. Keep theIdempotency-Keyon every write. - Platforms that invoice for many CUITs use partner accounts, which register each taxpayer with
POST /partner/taxpayers/and issue A or B for registered taxpayers and C for monotributo or exempt ones. Partner access is granted by contract.
Connect an AI assistant over MCP
The MCP server lives at https://api.yo-facturo.com/api/v1/mcp. It uses the Streamable HTTP transport and OAuth 2.1 with PKCE and dynamic client registration, so there is no token to paste.
- Add the server. In Claude Code run
claude mcp add --transport http yofacturo https://api.yo-facturo.com/api/v1/mcp. In Claude Desktop or claude.ai add a custom connector with the same URL. In ChatGPT add a custom MCP connector in developer mode. In Cursor add{"mcpServers": {"yofacturo": {"url": "https://api.yo-facturo.com/api/v1/mcp"}}}to~/.cursor/mcp.json. - Authorize. The client opens your browser; sign in to YoFacturo and approve
yofacturo:read,yofacturo:writeor both. A read-only connection can never modify anything. - Ask. For example, "list this month's sales" or "create a customer named ACME". Revoke a connection any time in the panel under Settings, Integrations, MCP server.
More on connecting each client is in the MCP guide.



