Overview
Platform payouts is for marketplaces and digital platforms that pay providers under the SAT digital-platforms scheme (Plataformas Tecnológicas: RESICO régimen625, retention key 26). Ride-hailing drivers, delivery couriers, lodging hosts and sellers of goods are all providers. The platform issues the CFDIs for a whole payout period from two files:
- a movements file, with one row per payout to a provider;
- a commissions file, with one row per provider per month holding the platform commission.
What it is not. This API is for a platform that invoices on behalf of its providers. If your company invoices its own sales, one CFDI per sale, use POST /invoices/income instead.
Base path: https://api.gigstack.io/v2/platform-payouts
A run goes through three steps:
- Plan.
POST /platform-payoutsuploads both files. The plan is computed during the request and returned in the response. Nothing is stamped yet. - Review.
GET /platform-payouts/{id}returns the totals.GET /platform-payouts/{id}/movementslists every row with what will be issued for it, or why nothing will be. - Confirm.
POST /platform-payouts/{id}/confirmhands the plan to the stamping worker. This cannot be undone. PollGET /platform-payouts/{id}until the run iscompletedorfailed, then read itsresultto see how it went.
Key Features
- Three documents per payout period - the provider’s income invoice, your retention certificate and your monthly commission invoice
- Tax policy per account - the regimes, SAT keys and withholding rates come from your account’s configuration (see Tax Policy)
- Synchronous planning - the plan, or the reason it failed, is in the response to the upload
- Idempotent uploads - a required
Idempotency-Keymakes a retried upload return the same run. If the key comes back with different files, it is refused - Row-level exclusions - a malformed or ineligible row is excluded with a reason and a stable code, and the other rows still go ahead
- A clear outcome - a finished run reports
result:completed,partially_completedorfailed - No double certification - a provider-month already certified by an earlier run is skipped
- Safe confirm - confirming twice returns the run’s status and does nothing else
Who Can Use It
Platform payouts is for marketplace master teams. All of these must hold:
On
POST /platform-payouts, these checks (and team_not_found / not_a_member) run right after the Idempotency-Key check and before the upload is read. The files of a refused request are never processed or stored.
Credentials:
- API keys and OAuth access tokens belong to the team. They can create, read and confirm that team’s runs.
- User-scoped tokens (MCP tokens, dashboard sessions) act as a person, who must be a member of the team (
403 forbidden/not_a_memberotherwise). The twoPOSTroutes also requireeditorpermission on invoices (403 forbidden,This action requires editor access to invoices). - gigstack Connect doesn’t apply. Runs always act on the credential’s own master team. Sending
?team=<connected team>moves the request to a team that isn’t a master team. Uploads and confirms are then refused withnot_master_team, and reads return404.
404 run_not_found, never 403, so an id can’t be used to discover someone else’s run.
What Gets Issued
For each movement (payout) and each provider-month, the plan decides which of three comprobantes to issue:
The monthly commission is prorated across the provider’s movements of that month, in proportion to each subtotal. That share is the movement’s
commission, and it is what the certificate reports. The commission invoice bills the full monthly figure.
Which documents a provider qualifies for depends on your account’s tax policy. Rows that don’t qualify are excluded with a Spanish reason and a stable code. See Exclusion codes.
Tax Policy
The documents a run issues, and the SAT keys and rates they carry, are set per master account when the account is onboarded. The same files can produce different CFDIs for a delivery platform and for a ride-hailing platform. The policy covers:- Eligibility: which tax regimes providers may be on, whether a provider needs a valid CSD (sellos) in gigstack, and whether a certificate may be issued to público en general (the generic RFC).
- Retention certificate: the service type (
tipoDeServ) and subtype (subTipServ) of the digital-platforms complement, and the ISR and IVA withholding rates. - Invoices: the product keys of the income and commission invoices, the unit, the concept descriptions, and the CFDI use, payment form, payment method and currency.
If your providers deliver goods, host lodging or provide another kind of service, contact support to configure your policy before your first live run. The policy can’t be changed through the API.
Input Files
Both files are sent in onemultipart/form-data request:
Rules for both:
- The format is chosen by extension (the MIME type is ignored).
.csvand.txtare read as comma-separated text (UTF-8, a BOM is fine)..xlsx,.xlsand.xlsmare read as workbooks, and only the first sheet is used. - Max 5 MB per file (
413 file_too_large) and max 50,000 rows (the plan fails). - The first row is the header. Headers match regardless of case, accents and repeated spaces (
CORREO ELECTRONICOmatchesCorreo electrónico). Extra columns are ignored. - Amounts are in MXN.
1250.00,1,250.00,1250,5and$1,250.00are all read. Any other value is a row problem, never a silent zero. - Column names: each column below has a primary name and a few accepted alternatives. Use the primary name in new files. When a column is missing, the error names it by its primary name.
Provider columns (both files)
When a file has more than one name column, the more specific one wins:
Nombre del proveedor or Nombre del conductor, then Razón social, then a bare Nombre.
Movements file columns
The provider columns, plus:Commissions file columns
The provider columns, plus:
Commissions are joined to movements on the provider id and the month. The commission invoice is excluded if the row has no
RFC. If a provider-month appears more than once, the first row is used.
A certificate needs a commission. The SAT rejects a retention certificate whose commission is zero (SPT147). A movement whose provider-month has no commission in the commissions file therefore gets no certificate.
File problems and row problems
- A file problem fails the whole plan. The run comes back as
plan_failedwith a Spanisherror. Examples: the file can’t be read, it’s empty, it has more than 50,000 rows, or required columns are missing (all missing columns are listed at once, for exampleAl archivo de comisiones le faltan estas columnas: Comisión.). - A row problem (an invalid date or amount, a subtotal below
0.01) only excludes that movement, with the problem as its reason.
Matching providers
Each row is matched to a team in your billing account. gigstack tries the provider id first (the team’smetadata.driverId), then the RFC, then the e-mail. When an RFC or e-mail matches more than one team, the row is excluded rather than guessed.
Run Status
Run Result
status: "completed" only means the worker has nothing left to try. To know how a finished run went, read result, not status. It is null until the run finishes (completed or failed).
How it’s counted:
- Failures are
progress.failed_countplusprogress.commission_failed_count. A movement counts as failed as soon as one of its documents (income invoice or certificate) failed. Commission invoices are counted separately, becausefailed_countcounts movements only. - Issued documents are
income_invoices_count+certificates_count+commission_invoices_count.
result existed get it derived from their counters by the same rule.
Endpoints
Create a Run
Form fields:
movements_file and commissions_file, both required. See Input Files.
201, with status: "plan_failed", zero counts and the reason in error:
Idempotency
The run id is derived from your team, the credential’s mode and theIdempotency-Key, and the run records a fingerprint of the two files’ contents (their bytes; the file names don’t count). So:
- The first request with a key creates the run:
201. - A later request with the same key and the same files returns that run, in whatever status it is now:
200. Nothing is planned again. - The same key with different files is refused:
409 idempotency_key_reused. The new files aren’t planned; send them under a new key. Renaming a file doesn’t change the fingerprint, and changing a single byte does. - Runs created before the fingerprint existed have none. A later request with their key is answered as before,
200with the existing run, whatever files it carries. - A retry that arrives while the first request is still planning gets the run in
planning. PollGET /platform-payouts/{id}; don’t upload again. - A plan that failed stays failed under its key. Fix the file and upload with a new key.
- The same key used with a test key and with a live key names two different runs.
planning for more than 10 minutes (the instance planning it died), a retry with the same key takes it over and plans it again from the files in that retry.
Use one key per distinct upload, for example payouts-2026-08-v1, then payouts-2026-08-v2 after a fix.
Get a Run
progress fills in as the worker stamps, and result is set once the run finishes.
List Movements
Note the nested shape: the array is at
data.data and the cursor at data.next. Keep requesting with next while data.has_more is true. The cursor stays valid while the worker updates statuses, so pages never overlap or skip rows.
reason_code: "missing_series"). A movement is excluded only when neither document is planned; then exclusion_codes lists the distinct codes of both.
Commission invoices are not listed per provider-month; the run only reports how many were planned (planned_documents.commission), stamped (progress.commission_invoices_count) and failed (progress.commission_failed_count).
Confirm a Run
Irreversible. Confirming hands the plan to the stamping worker. A stamped CFDI can only be cancelled, not undone. Review the plan first.No request body. Answers as soon as the run is handed over; the stamping itself happens in the background (the worker runs every minute).
- Safe to retry. Confirming a run that is already
stampingorcompletedreturns200with its current status and does nothing else. - Only a
plan_readyrun with at least one planned document can be confirmed. Otherwise:409 plan_not_ready(still planning),409 not_confirmable(plan_failedorfailed),409 nothing_to_stamp(every document was excluded). - The entitlement is checked again: if platform payouts was disabled on the billing account since the upload, the confirm is refused with
403 feature_disabled.
Provider-months already certified
A provider’s monthly commission is prorated over the movements of this file. If an earlier run already certified the same provider and month, certifying it again would report the platform commission twice to the SAT. So, when you confirm, the run reserves every provider-month it certifies:- A provider-month held by an earlier confirmed run of your team (same mode) is given up: those movements’
certificatebecomesskipped, withreason_code: "certificate_month_reserved"and a reason naming the earlier run (La corrida batchrun_… ya reservó las constancias de 2026-08 para este proveedor). - The run’s
included_count,excluded_count,exclusion_summary,exclusion_code_summaryandplanned_documents.certificateare recomputed to match. A movement left with nothing to issue becomesexcluded, withexclusion_codes: ["certificate_month_reserved"]. - Income and commission invoices are not affected.
La corrida … ya emitió constancias de … para este proveedor: vuelve a subir el mes completo o emítelas por separado.
To certify a month correctly, upload all of the provider’s movements for that month in one run.
Following progress
PollGET /platform-payouts/{id} every 30-60 seconds. The worker stamps one document at a time, with a short pause between stamps, so a run of several hundred movements takes several minutes or more.
progress.stamped_count/progress.failed_countcount movements.progress.income_invoices_count,certificates_countandcommission_invoices_countcount comprobantes;commission_failed_countcounts failed commission invoices.income_invoices_amountis the total of the stamped income invoices in MXN.- When the run finishes, read
result. If it ispartially_completedorfailed, list the movements and look forstatus: "failed", the document’serror(Spanish, for people) and itserror_code(for programs). - A transient failure (for example the PAC being unavailable,
error_codeNETWORK_ERRORorHTTP_503) is retried, up to 3 attempts per document; while it waits, the document staysplannedwith the lasterroranderror_code. A SAT rejection is final; itserror_codeis the CFDI error code (for exampleSTAMPING_ERROR). - A stamp that was sent to the PAC but never recorded fails with
error_code: "interrupted_stamp"andTimbrado interrumpido: verificar en el PAC antes de reintentar. It is not retried automatically, because retrying could issue a second CFDI for the same payout. Contact support. - If a provider’s team was moved out of your billing account, or scheduled for deletion, after the plan was made, its documents fail with
error_code: "team_out_of_scope"and nothing is issued for it (La cuenta del proveedor ya no pertenece a esta cuenta de facturación: no se emitió nada.orLa cuenta del proveedor está programada para eliminarse: no se emitió nada.). If the team was deleted, theerrorisLa cuenta del proveedor ya no existe en Gigstack.
uuid is the SAT folio fiscal. For a certificate, invoice_id is the retention’s id, readable with GET /retentions/{id}. For income, invoice_id is an invoice of the provider’s team (provider.team), not of yours; read it with the team parameter if your plan includes gigstack Connect.
End-to-End Example
Use Bash,curl and jq, with GIGSTACK_API_KEY loaded locally and the input files
reviewed for the intended team and mode. These scripts do not retry failed writes
automatically. An HTTP or JSON error stops the script; reconcile any ambiguous write
before starting again.
RUN_ID and BASE).
A plan_failed, empty or unexpected plan is not ready to confirm.
Response Objects
Run
Movement
Document state (income, certificate)
Exclusion codes
Every reason a document isn’t planned comes with a stable code:reason_code on the document, exclusion_codes on an excluded movement, and the keys of the run’s exclusion_code_summary. Branch on the codes. Codes are never renamed (new ones may be added); the Spanish sentences in reason, exclusion_reason and exclusion_summary are for people and may be reworded at any time.
Error Handling
All errors use the standardized envelope. Messages are in Spanish (a few validation messages are in English); branch onerror.code, not on the message.
401, and the 403 for a revoked key or a plan without API access) come from the authentication layer with a raw { "message": … } body; see Authentication errors.
A problem inside a file isn’t an HTTP error: the upload answers
201 with status: "plan_failed" and the reason in data.error.
Best Practices
- Use one
Idempotency-Keyper upload and reuse it only to retry that same upload (same file contents). After fixing a file, use a new key; reusing the old one is409 idempotency_key_reused. - Review before confirming. Check
exclusion_code_summaryand list the excluded movements; fix provider data in gigstack or the files and upload again rather than confirming a plan with surprises. - Upload whole months. Certificates prorate the monthly commission over the movements in the file. A provider-month split across runs is only certified by the first.
- Include the commissions for every month in the movements file, or those movements get no certificate.
- Try it with a test key first. Test runs are separate from live runs.
- Poll gently. Every 30-60 seconds is enough; the worker runs once a minute.
- Read
result, notstatus, when the run finishes. Onpartially_completedorfailed, list the failed movements and branch on each document’serror_code. - Branch on codes, not sentences:
error.code,reason_code,exclusion_codes,error_code.
Related Resources
- Retentions API - Read the retention certificates a run issued
- Invoices API - Income and commission invoices
- gigstack Connect - Read documents of the providers’ teams
- CFDI Errors Reference - Stamping errors you may see in a document’s
error - Test Mode
For additional assistance, contact support@gigstack.io