# Issuance and download

> Poll until the certificate is issued, then read its details and download the certificate files.

## Step 3 — poll for issuance

### PUT /api/v1/ssls/check-status

Asks the CA where the order stands and updates our record. It is a `PUT` because it has that side effect.

**Request**

```json
{
  "domainName": "apiname.com"
}
```

**Response — still validating**

```json
{
  "domainName": "apiname.com",
  "status": "WaitingForValidation",
  "statusMessage": "Waiting for domain control validation",
  "isConfigured": true,
  "isIssued": false,
  "certificateExpiresAt": null
}
```

**Response — issued**

```json
{
  "domainName": "apiname.com",
  "status": "Active",
  "statusMessage": "The certificate is active",
  "isConfigured": true,
  "isIssued": true,
  "certificateExpiresAt": "2027-11-28T23:59:59"
}
```

:::tip[Poll on `isIssued`, not on `status`]
`isIssued: true` is the signal that the certificate exists and can be downloaded. Expected waits:

- **DV with Cname or Http** — minutes after the record or file is in place.
- **DV with Email** — as long as the customer takes to click.
- **OV** — 1 to 3 business days; the CA verifies the organisation.
- **EV** — 3 to 10 business days.

Poll every **5 to 10 minutes**, not every second — this call costs 10 quota points. For OV and EV, hourly is plenty.
:::

**Certificate statuses**

| Value | Name | What to do |
| --- | --- | --- |
| 1 | `Active` | Issued and in force. Download it. |
| 2 | `WaitingForInstall` | Bought but not configured. **The 7-day clock is running.** |
| 3 | `InstallationInProgress` | Installation under way. |
| 4 | `WaitingForValidation` | Publish the DNS record or serve the file, then keep polling. |
| 5 | `ConfirmationSent` | Approval e-mail sent. Chase the customer. |
| 6 | `WaitingForDocument` | The CA wants organisation documents (OV/EV vetting). They will contact the customer directly. |
| 7 | `Cancelled` | Revoked, cancelled, or the 7-day window lapsed. |
| 8 | `ConfigurationStarted` | Order sent to the CA, outcome not yet recorded. **Do not call configure again.** Keep polling. |
| 10 | `Renewed` | Superseded by a renewal. Still valid until its own expiry, but the current certificate is the newer one. |

**Errors**

| Code | Meaning and fix |
| --- | --- |
| `Ssl:10026` | No certificate for this domain. |
| `Ssl:10049 / Ssl:10050` | The CA could not be reached or rejected the query. Retry later. |

## Steps 4 and 5 — detail and download

### GET /api/v1/ssls/certificate

Full detail for the certificate on a domain. Use it to build the customer's certificate page.

**Request**

```http
GET /api/v1/ssls/certificate?domainName=apiname.com
```

**Response — 200**

```json
{
  "domainName": "apiname.com",
  "status": "Active",
  "isConfigured": true,
  "isIssued": true,
  "packageName": "PositiveSSL",
  "packageCode": "POSITIVESSL",
  "subscription": {
    "purchasedAt": "2026-10-30T16:12:40",
    "expiresAt": "2027-10-30T16:12:40"
  },
  "configuration": {
    "configuredAt": "2026-10-30T16:28:11",
    "configurationLink": null,
    "pin": null,
    "webServer": "Apache",
    "authenticationType": "Cname",
    "sanList": null,
    "wildcardList": null,
    "approverMail": "admin@apiname.com",
    "csr": "-----BEGIN CERTIFICATE REQUEST-----\n...\n-----END CERTIFICATE REQUEST-----",
    "dcvDetails": "{\"MD5\":\"A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6\"}"
  },
  "certificate": {
    "issuedAt": "2026-10-30T16:41:55",
    "validUntil": "2027-11-28T23:59:59"
  },
  "contacts": [
    { "type": "Administration", "firstName": "Ahmet", "lastName": "Yilmaz" },
    { "type": "Technical",      "firstName": "Ahmet", "lastName": "Yilmaz" }
  ]
}
```

:::danger[Two different expiry dates — do not confuse them]
| Field | Meaning |
| --- | --- |
| `subscription.expiresAt` | **What you paid for.** Drive renewal reminders off this. |
| `certificate.validUntil` | **What the CA signed.** This is when browsers start showing warnings. |

They diverge when configuration happens days after purchase. Use `subscription.expiresAt` for billing and the renewal window, and `certificate.validUntil` for "your certificate expires in N days" warnings.
:::

`configuration` is `null` until configured; `certificate` is `null` until issued. Check for null before reading them.

### GET /api/v1/ssls/certificate-content

Returns the issued certificate and its chain. Call it only once `isIssued` is `true`.

**Request**

```http
GET /api/v1/ssls/certificate-content?domainName=apiname.com
```

**Response — 200**

```json
{
  "certificate": "-----BEGIN CERTIFICATE-----\nMIIFXzCCBEegAwIBAgIR...\n-----END CERTIFICATE-----",
  "intermediateCertificate1": "-----BEGIN CERTIFICATE-----\nMIIGEzCCA/ugAwIBAgIQ...\n-----END CERTIFICATE-----",
  "intermediateCertificate2": "-----BEGIN CERTIFICATE-----\nMIIF3jCCA8agAwIBAgIQ...\n-----END CERTIFICATE-----",
  "rootCertificate": "-----BEGIN CERTIFICATE-----\nMIIFgTCCA2mgAwIBAgIO...\n-----END CERTIFICATE-----"
}
```

:::tip[Building the bundle to install]
For nginx, Apache and most modern servers, concatenate in this order:

```text
certificate
intermediateCertificate1
intermediateCertificate2   (if present)
```

The **root is normally not included** — clients already trust it, and shipping it just wastes bytes in every handshake. `intermediateCertificate2` is `null` for brands that use a single intermediate; skip it if so.

The **private key is not returned here.** It is the one you kept from `generate-csr`, or the one you supplied to `configure`.
:::

**Errors**

| Code | Meaning and fix |
| --- | --- |
| `Ssl:10026` | No certificate for this domain. |
| `Ssl:10048` | Not issued yet. Keep polling `check-status`. **Note:** this code is shared with a provider-order error, so the message you see may mention a provider order number — if `isIssued` is `false`, the real cause is simply that the certificate is not ready. |
