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 (if not already exists). Bearer tokens last 1 hour and can be reused across multiple requests; 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.