In the test environment nothing is really invoiced: there’s no OCR, merchants aren’t contacted and no CFDI is issued. Instead, a simulator moves each ticket through a status path that you choose with the last 4 digits of the card (CardLastFour) when you upload it. That way you can test your integration (queries, reports and webhooks) against several possible outcomes, with predictable timing.
The simulator only exists in the test environment. In production, tickets follow the real flow described in Ticket flow and CardLastFour is only used for invoicing.
Test environment
| Production | Test | |
|---|---|---|
| Base URL | https://api.fotofacturas.ai/B2B/ | https://api-staging.fotofacturas.ai/B2B/ |
| Token | Production token. Find it in the business web app, under API y plan › Token de API. | Test token. Find it in the test business web app, under API y plan › Token de API. |
| Ticket processing | OCR, automatic invoicing, email with the merchant and agents | Simulator (this document) |
| Webhooks | Sent | Sent |
- Each environment’s data is separate: test associations, users, RFCs, webhooks and tickets don’t exist in production, and vice versa.
- APIs, permissions, validations and plan limits are the same as in production. A test ticket does count toward the test plan’s monthly limit.
How it works
The simulator checks tickets every minute. On each check it moves every ticket one single step along its path, as long as the wait time since its last status change has passed.
The last 4 digits of the card define the scenario:
| Position | Example 0 0 3 7 | Meaning |
|---|---|---|
| 1st digit | 0 | Ignored. |
| 2nd digit | 0 | Ignored. |
| 3rd digit | 3 | Minutes to wait between each status change (0 to 9). |
| 4th digit | 7 | Scenario (0 to 9): status path and final result. |
With 0037 the ticket follows scenario 7 and changes status every 3 minutes.
CardLastFour is only required with a credit (04) or debit (28) card, but the API accepts it with any payment method. In test, always send it, even if the method is cash (01), to choose the scenario.
Scenarios
Every ticket starts at 1 CREATED. The numbers are the ticketStatusId (see Catalogs).
Not invoiceable (end at 10 UNSIGNABLE)
| 4th digit | Path | Final ticketCodeId |
|---|---|---|
0 | 1 → 3 → 10 | 4 · Not a purchase ticket |
1 | 1 → 3 → 5 → 10 | 5 · Expired ticket |
2 | 1 → 3 → 6 → 10 | 8 · Illegible ticket |
3 | 1 → 3 → 6 → 5 → 10 | 19 · Couldn’t reach the merchant |
4 | 1 → 3 → 6 → 8 → 10 | 15 · Not identified |
Invoiced (end at 11 SIGNED)
| 4th digit | Path | Final ticketCodeId |
|---|---|---|
5 | 1 → 3 → 11 | 2 · Invoiced |
6 | 1 → 3 → 5 → 11 | 2 · Invoiced |
7 | 1 → 3 → 6 → 11 | 2 · Invoiced |
8 | 1 → 3 → 6 → 5 → 11 | 2 · Invoiced |
9 | 1 → 3 → 6 → 8 → 11 | 2 · Invoiced |
On reaching the final status, the ticket ends with invoiceMethod: 2 (automatic) and the ticketCodeId from the table.
Special cases
| Situation | Result |
|---|---|
CardLastFour wasn’t sent or isn’t 4 digits | On the first check it moves to 3 → 10 with code 15 (Not identified), without waiting. |
| The ticket reaches a status that isn’t on its path (for example, an agent moved it) | It moves to 3 → 10 with code 15, without waiting. |
| The ticket was archived | The simulator ignores it. |
Timing
Each step happens on the first check (every minute) after the wait time since the last change has passed. Approximate time to the final status:
steps in the path × max(3rd digit, 1) minutes, with up to 1 extra minute per step.
| Card | Steps | Approximate time |
|---|---|---|
0005 | 2 (1 → 3 → 11) | 2 to 4 min |
0009 | 4 (1 → 3 → 6 → 8 → 11) | 4 to 8 min |
0050 | 2 (1 → 3 → 10) | 10 to 12 min |
0099 | 4 | 36 to 40 min |
Use 0 or 1 as the third digit for quick tests, and a higher value when you want time to check each intermediate status with GetTicketById and each webhook you receive.
What you get in each scenario
Webhooks
The same events as in production are generated (see Webhooks):
ticket.status_changedwhen the ticket is created (1) and on every status change along the path.ticket.invoicedon reaching11.ticket.failedon reaching10.
Example with 0006 (scenario 6) and all three events configured:
| Change | Events |
|---|---|
null → 1 | ticket.status_changed |
1 → 3 | ticket.status_changed |
3 → 5 | ticket.status_changed |
5 → 11 | ticket.status_changed + ticket.invoiced |
Ticket data (GetTicketById)
| Field | In test |
|---|---|
ticketStatusId / ticketCodeId / invoiceMethod | Per the scenario. |
ticketUrl | The image you uploaded. |
pdfUrl, xmlUrl, xmlData | In the invoiced scenarios (5 to 9), the simulator attaches a real generic invoice, always the same one. In the not-invoiceable ones they’re empty. |
ticketDate, ticketAmount | In the invoiced scenarios, the date and total of that generic invoice. |
Reports (GetPeriodStats, GetTicketStatuses, GetTicketsByMonth…) count test tickets the same way as in production.
Guide for your development
-
Get your test token in the test business web app, under API y plan.
-
Set up data: create an association (
CreateAssociation), a user (CreateUser) and register an RFC with its CSF (AddUserTaxInfoorSaveAssociationCSF). -
Configure your webhooks (
SaveWebhook), store the secret and validate the signature withTestWebhook. -
Upload one ticket per scenario with
CreateUserTicketorCreateAssociationTicket, varyingCardLastFour.The image can be any valid file (JPG, JPEG, HEIC, WEBP, PNG, PDF, max. 10 MB): the simulator doesn’t read it.
-
Check that your system receives and processes every event, and that the final status matches
GetTicketById.
Recommended minimum set:
| Card | To test |
|---|---|
0015 | Invoiced directly. |
0019 | Invoiced with several intermediate statuses. |
0010 | Not invoiceable directly. |
0013 | Not invoiceable with several intermediate statuses. |
no CardLastFour | Special case: ends at 10 with code 15. |
To test webhook retries, have your server respond with an error (for example, 500) to some event and review the attempts with GetWebhookEventById or in the business web app; then resend it with Reenviar evento on the Webhooks screen.