Skip to content

AI-ready page export

Integration Walkthrough

Follow a complete FirstCyber quote from authentication through policy issuance. Each step shows a copy-pasteable request and response. For endpoint field reference, see the FirstCyber Quote Service.

Sandbox first

Run this walkthrough against https://api-sandbox.k2cyber.co with sandbox Partner Portal credentials. For Direct Bill bind testing, use direct@k2cyber.ai as the broker email. See Quick Start for environment setup.

1. Authenticate

Obtain a bearer token with your M2M client credentials. Cache the token and refresh before expires_in elapses.

bash
curl -X POST https://api-sandbox.k2cyber.co/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&grant_type=client_credentials&scope=quote"
javascript
const body = new URLSearchParams({
  client_id: process.env.K2_CLIENT_ID,
  client_secret: process.env.K2_CLIENT_SECRET,
  grant_type: "client_credentials",
  scope: "quote",
});

const response = await fetch("https://api-sandbox.k2cyber.co/auth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body,
});

const { access_token, expires_in } = await response.json();
python
import os
import requests

response = requests.post(
    "https://api-sandbox.k2cyber.co/auth/token",
    data={
        "client_id": os.environ["K2_CLIENT_ID"],
        "client_secret": os.environ["K2_CLIENT_SECRET"],
        "grant_type": "client_credentials",
        "scope": "quote",
    },
)
token = response.json()

Every subsequent request needs both:

  • Authorization: Bearer <access_token>
  • x-integration-key: <INTEGRATION-KEY>

Credentials come from the Partner Portal.

2. Submit a quote

Send underwriting information to create a quote. The response includes a quote_id and an initial quote_status.

bash
curl -X POST https://api-sandbox.k2cyber.co/quote/firstcyber/submit \
  -H "x-integration-key: <INTEGRATION-KEY>" \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "broker_email": "broker@example.com",
    "insured_name": "Acme Widgets LLC",
    "insured_location": {
      "address_line1": "510 Meadowmont Village Circle #142",
      "address_city": "Chapel Hill",
      "address_state": "NC",
      "address_zip": "27514"
    },
    "claims": { "claims_count": 0, "claims_amount": 0 },
    "effective_date": "2025-11-01",
    "revenue": 5000000,
    "naics": 722515,
    "question_highrisk": false,
    "agg_limit": 1000000,
    "retention": 2500
  }'
javascript
const response = await fetch("https://api-sandbox.k2cyber.co/quote/firstcyber/submit", {
  method: "POST",
  headers: {
    "x-integration-key": process.env.K2_INTEGRATION_KEY,
    Authorization: `Bearer ${access_token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    broker_email: "broker@example.com",
    insured_name: "Acme Widgets LLC",
    insured_location: {
      address_line1: "510 Meadowmont Village Circle #142",
      address_city: "Chapel Hill",
      address_state: "NC",
      address_zip: "27514",
    },
    claims: { claims_count: 0, claims_amount: 0 },
    effective_date: "2025-11-01",
    revenue: 5000000,
    naics: 722515,
    question_highrisk: false,
    agg_limit: 1000000,
    retention: 2500,
  }),
});

const result = await response.json();
python
import os
import requests

response = requests.post(
    "https://api-sandbox.k2cyber.co/quote/firstcyber/submit",
    headers={
        "x-integration-key": os.environ["K2_INTEGRATION_KEY"],
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
        "broker_email": "broker@example.com",
        "insured_name": "Acme Widgets LLC",
        "insured_location": {
            "address_line1": "510 Meadowmont Village Circle #142",
            "address_city": "Chapel Hill",
            "address_state": "NC",
            "address_zip": "27514",
        },
        "claims": {"claims_count": 0, "claims_amount": 0},
        "effective_date": "2025-11-01",
        "revenue": 5000000,
        "naics": 722515,
        "question_highrisk": False,
        "agg_limit": 1000000,
        "retention": 2500,
    },
)
result = response.json()

Save data.quote_id — every later step uses it. Rating may still be in progress when submit returns with quote_status: "pending"; poll or wait for webhooks until the quote is ready.

3. Wait for rating (poll or webhook)

Choose one approach based on your architecture.

Option A — Poll status

Call GET /status/{id} every 5–10 seconds until quote_status is ready_to_bind (or another terminal / decision state).

bash
curl -X GET "https://api-sandbox.k2cyber.co/quote/firstcyber/status/123e4567-e89b-12d3-a456-426614174000" \
  -H "x-integration-key: <INTEGRATION-KEY>" \
  -H "Authorization: Bearer <TOKEN>"
javascript
const response = await fetch(
  "https://api-sandbox.k2cyber.co/quote/firstcyber/status/123e4567-e89b-12d3-a456-426614174000",
  {
    method: "GET",
    headers: {
      "x-integration-key": process.env.K2_INTEGRATION_KEY,
      Authorization: `Bearer ${access_token}`,
    },
  },
);

const status = await response.json();
python
import os
import requests

response = requests.get(
    "https://api-sandbox.k2cyber.co/quote/firstcyber/status/123e4567-e89b-12d3-a456-426614174000",
    headers={
        "x-integration-key": os.environ["K2_INTEGRATION_KEY"],
        "Authorization": f"Bearer {access_token}",
    },
)
status = response.json()

Stop polling once status is ready_to_bind, approved_rate, underwriter_review, declined, or failed. See Quote Status Values and the Quote Status Flow.

Option B — Subscribe to webhooks

Subscribe early in the Webhook Portal so you receive lifecycle events without polling:

EventMeaning
quote.createdQuote finished rating; status is available
quote.proposalReadyQuote proposal PDF is ready to download
policy.createdPolicy issued after bind

4. Download the quote proposal (optional)

Once rating completes and the proposal is available (quote.proposalReady, or status ready_to_bind / approved_rate / underwriter_review), download the PDF.

bash
curl -X GET "https://api-sandbox.k2cyber.co/quote/firstcyber/document/quote-proposal/stream/123e4567-e89b-12d3-a456-426614174000" \
  -H "x-integration-key: <INTEGRATION-KEY>" \
  -H "Authorization: Bearer <TOKEN>" \
  -o quote-proposal.pdf
javascript
const response = await fetch(
  "https://api-sandbox.k2cyber.co/quote/firstcyber/document/quote-proposal/stream/123e4567-e89b-12d3-a456-426614174000",
  {
    method: "GET",
    headers: {
      "x-integration-key": process.env.K2_INTEGRATION_KEY,
      Authorization: `Bearer ${access_token}`,
    },
  },
);

const buffer = Buffer.from(await response.arrayBuffer());
require("fs").writeFileSync("quote-proposal.pdf", buffer);
python
import os
import requests

response = requests.get(
    "https://api-sandbox.k2cyber.co/quote/firstcyber/document/quote-proposal/stream/123e4567-e89b-12d3-a456-426614174000",
    headers={
        "x-integration-key": os.environ["K2_INTEGRATION_KEY"],
        "Authorization": f"Bearer {access_token}",
    },
)
with open("quote-proposal.pdf", "wb") as f:
    f.write(response.content)

A 409 means the PDF is not ready yet — wait for the webhook or poll status, then retry. Full availability rules are in GET /document/quote-proposal/stream/{id}.

5. Bind the quote

When status is ready_to_bind, you may initiate a bind. The API returns 202 Accepted immediately; issuance continues asynchronously.

bash
curl -X PUT "https://api-sandbox.k2cyber.co/quote/firstcyber/bind/123e4567-e89b-12d3-a456-426614174000" \
  -H "x-integration-key: <INTEGRATION-KEY>" \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{}'
javascript
const response = await fetch(
  "https://api-sandbox.k2cyber.co/quote/firstcyber/bind/123e4567-e89b-12d3-a456-426614174000",
  {
    method: "PUT",
    headers: {
      "x-integration-key": process.env.K2_INTEGRATION_KEY,
      Authorization: `Bearer ${access_token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({}),
  },
);

const result = await response.json();
python
import os
import requests

response = requests.put(
    "https://api-sandbox.k2cyber.co/quote/firstcyber/bind/123e4567-e89b-12d3-a456-426614174000",
    headers={
        "x-integration-key": os.environ["K2_INTEGRATION_KEY"],
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={},
)
result = response.json()

6. Get the policy document

After policy.created fires — or GET /status/{id} returns issued — download the policy PDF.

bash
curl -X GET "https://api-sandbox.k2cyber.co/quote/firstcyber/document/policy/stream/123e4567-e89b-12d3-a456-426614174000" \
  -H "x-integration-key: <INTEGRATION-KEY>" \
  -H "Authorization: Bearer <TOKEN>" \
  -o policy.pdf
javascript
const response = await fetch(
  "https://api-sandbox.k2cyber.co/quote/firstcyber/document/policy/stream/123e4567-e89b-12d3-a456-426614174000",
  {
    method: "GET",
    headers: {
      "x-integration-key": process.env.K2_INTEGRATION_KEY,
      Authorization: `Bearer ${access_token}`,
    },
  },
);

const buffer = Buffer.from(await response.arrayBuffer());
require("fs").writeFileSync("policy.pdf", buffer);
python
import os
import requests

response = requests.get(
    "https://api-sandbox.k2cyber.co/quote/firstcyber/document/policy/stream/123e4567-e89b-12d3-a456-426614174000",
    headers={
        "x-integration-key": os.environ["K2_INTEGRATION_KEY"],
        "Authorization": f"Bearer {access_token}",
    },
)
with open("policy.pdf", "wb") as f:
    f.write(response.content)

Next steps

Maintained by the K2 Cyber Insurance engineering team.