# Integration Guide — Partner API > The full flow to integrate Firma Digital as a partner — issue purchase_codes, redeem identity, and retrieve the certificate. This guide covers the full lifecycle of a purchase_code: issuing it, redeeming it (validating the holder's identity), and retrieving the already-issued certificate. There are two separate APIs, on two different servers: - **Purchase Code API** (`partner-pcode`, Cloudflare Worker, `/api/partner/v1`) — generates and lists purchase_codes. - **Partner API** (`/api/partners/v1`) — redeems identity and delivers certificates. ## 1. Your configuration decides the flow, not the endpoint Unlike previous versions of this API, **there is a single redeem endpoint** (`certificate_request`). Its behavior adapts automatically based on your partner configuration: | Your configuration | What happens in `certificate_request` | | --- | --- | | `require_cedula_serie = 0` (full flow, default) | You must send `names`/`fLastname`/`phone`. Validated against Clave Única in addition to the civil registry. | | `require_cedula_serie = 1` (reduced flow) | **Don't** send `names`/`fLastname`/`phone` — they come from the civil registry. You must send `serialNumber` (ID card serial). No Clave Única. | | `mode = 1` (B2C, default) | The certificate is only delivered once you call `certificate_claim`. | | `mode = 2` (B2B) | The certificate is generated and delivered automatically (webhook/email) in the same `certificate_request` call — **don't call `certificate_claim`**, it will always say "already issued". | These two settings are independent — you can have reduced flow + B2B, full flow + B2C, or any other combination. Contact support to configure them. ## 2. B2C flow (the most common one) 1. **Issue a purchase_code** — `POST /pcode` on the Purchase Code API. ```bash curl -X POST https://partner-pcode.tsp.workers.dev/api/partner/v1/pcode \ -H "x-gw: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"productCode": "FV01", "external_reference_id": "order-12345"}' ``` This call can fail with `400` if your partner doesn't have that `productCode` enabled — each partner can be restricted to a subset of products (`FV00`-`FV03`). If you're not sure which products you have access to, contact support. 2. **Redeem the purchase_code** — `POST /certificate_request`. Which fields you send depends on your flow (see table above). Example with the reduced flow: ```bash curl -X POST https://api.firma.digital/api/partners/v1/certificate_request \ -H "x-gw: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "pCode": "MYPARTNER-26-FV01-A1B2C3D4", "referenceId": "order-12345", "dni": "18925910-7", "serialNumber": "536463312", "email": "user@example.com", "pin": "1234" }' ``` The response comes back with `status: "VALIDATED"` — the certificate **doesn't exist yet**, only identity was verified. 3. **Retrieve the certificate** — `POST /certificate_claim`, with the same `pin` used in the previous step (it doubles as the PFX's password). ```bash curl -X POST https://api.firma.digital/api/partners/v1/certificate_claim \ -H "x-gw: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"p_code": "MYPARTNER-26-FV01-A1B2C3D4", "dni": "18925910-7", "pin": "1234"}' ``` This call is **one-time only**: calling it again responds `201` with `ALREADY_ISSUED` instead of regenerating the certificate. If you need to recover it after it was already claimed, contact support. ## 3. B2B flow Same as steps 1 and 2 above, except step 2 is already the last one: if your configuration has `mode = 2`, the `certificate_request` response comes back with `status: "EMITTED"` and the certificate is delivered automatically through your configured webhook and/or email — both can fire at once, it's not either/or. There is no step 3. ## 4. Checking status `GET /status?p_code=...` — most useful if you integrated via the **widget** (the user completes the flow in their own browser, not in a single call you make) and you need to know how far along they are without listening to widget events in real time. ```bash curl "https://api.firma.digital/api/partners/v1/status?p_code=MYPARTNER-26-FV01-A1B2C3D4" \ -H "x-gw: $API_KEY" ``` Possible states, in precedence order (the first one that applies wins): | State | Meaning | | --- | --- | | `CANCELLED` | Voided (order cancelled) — wins even over an already-issued certificate. | | `EXPIRED` | The certificate was issued but has since expired. | | `EMITTED` | Certificate generated and delivered. | | `LOCKED` | Blocked before it got issued — if it was already issued, this doesn't show: it still reports `EMITTED`. | | `IN_ONBOARDING` | The user is midway through the widget flow. | | `VALIDATED` | Identity verified (B2C only), `certificate_claim` still pending. | | `ACTIVE` | Exists, nobody has redeemed it yet. | | `NOT_FOUND` | The purchase code doesn't exist. | ## 5. Common errors | Code | Cause | | --- | --- | | `401` | Invalid or missing API Key. | | `404` | The purchase_code doesn't exist (or, in `certificate_claim`, exists but never went through `certificate_request`). | | `406` | Business rejection — the `message` field says which: serial required, RUT mismatch, duplicate email, ID card doesn't match the civil registry, expiration date mismatch, etc. | | `423` | The purchase_code is permanently blocked (voided). | | `429` | Rate limit — 10 emissions per minute per partner, or ID validation attempts exhausted (temporary security lock). | See the [Partner API Reference](/en/api/partners/) and the [Purchase Code API Reference](/en/api/pcode/) for full details on each endpoint.