# Inbound API

### How it works

Kernel operates differently from static database providers (like ZoomInfo/Cognism).

We collect and classify data in real time, which means the process is asynchronous.

You initiate an enrichment task and periodically poll for results as data becomes available.

There are two main ways to use inbound enrichment:

* **API‑triggered inbound** — Your systems call the Inbound API directly for just‑in‑time enrichment of a specific record, then poll for results.
* **Scheduled inbound via your CRM** — Kernel polls configured objects in your CRM (for example, Salesforce) on a schedule, automatically enriching new or updated records that meet your criteria.

#### Understanding Data Availability (Fast vs. Slow)

* **Asynchronous Population**: The output JSON payload populates gradually. When you poll, fields for which data hasn't been found or processed yet will typically have a value of `null`.
* **Fast Data Points**: For example, certain firmographic data points are usually available relatively quickly (potentially within minutes). These often include:
  * Country
  * State/Region
  * Industry
  * Headcount
  * Headcount Growth
* **Slower Data Points**: Other data points may require more extensive analysis or different data sources and can take longer (potentially hours) to populate.
* **Polling Strategy**: Your polling logic should handle partially populated payloads and continue polling until the `status` is `complete` or `error`. The specifics regarding the fast/slow data points depend on your custom setup.

#### Error Handling Strategy

When interacting with the API, you might encounter different types of errors. Here's the recommended default strategy:

1. **HTTP 502/503/504 Server Errors**:
   * **Meaning**: These indicate a temporary issue, either on the server-side.
   * **Default Action**: Retry the request after some time.
2. **GET Response** `"status": "error"`:
   * **Meaning**: The specific enrichment workflow identified by the `executionId` encountered an error during processing.
   * **Default Action**: Retry the request after some time, if the error persists, contact Kernel support.

#### Rate Limits

The API enforces rate limiting to ensure fair usage:

* **Rate Limit**: 5 requests per second per API key
* **Concurrency Limit**: Maximum of 200 accounts can be processing simultaneously
* **429 Response**: When rate limit is exceeded, you'll receive a 429 status code. Please wait before retrying.

#### Base path

```
api.kernel.ai
```

## Initiate asynchronous enrichment for an account

> \### Input Data Requirements:\
> \* \_\_salesforceId\_\_ (string): This is the Salesforce record ID (e.g., Lead IDs often start with 00Q, Account IDs with 001).\
> \* You must provide either:\
> &#x20; \* \_\_linkedinUrl\_\_ (string): The LinkedIn profile URL (e.g., <https://www.linkedin.com/company/example).\\>
> &#x20; \* OR both \_\_name\_\_ (string) and \_\_website\_\_ (string).\
> \* \_\_force\_\_ (boolean, optional): When true, bypasses the previously enriched check and starts a fresh enrichment.\
> \* Contextual Data (Optional but helpful): While the core requirement is above, providing additional context like emailDomain (e.g., kernel.ai from <marcus@kernel.ai>) will be beneficial depending on the specific workflow configuration, even if it is not strictly required by the current endpoint setup. Check with your Kernel contact to see if these are utilized.\
> \* Data Privacy: Do not send Personally Identifiable Information (PII) such as first name or last name, unless explicitly part of the agreed schema. You should send email\_domain if available and relevant to the configuration.\
> &#x20;

```json
{"openapi":"3.1.0","info":{"title":"Kernel Inbound API","version":"1.0.0"},"paths":{"/api/v1/inbound/enrichment":{"post":{"tags":["Enrichment"],"summary":"Initiate asynchronous enrichment for an account","description":"### Input Data Requirements:\n* __salesforceId__ (string): This is the Salesforce record ID (e.g., Lead IDs often start with 00Q, Account IDs with 001).\n* You must provide either:\n  * __linkedinUrl__ (string): The LinkedIn profile URL (e.g., https://www.linkedin.com/company/example).\n  * OR both __name__ (string) and __website__ (string).\n* __force__ (boolean, optional): When true, bypasses the previously enriched check and starts a fresh enrichment.\n* Contextual Data (Optional but helpful): While the core requirement is above, providing additional context like emailDomain (e.g., kernel.ai from marcus@kernel.ai) will be beneficial depending on the specific workflow configuration, even if it is not strictly required by the current endpoint setup. Check with your Kernel contact to see if these are utilized.\n* Data Privacy: Do not send Personally Identifiable Information (PII) such as first name or last name, unless explicitly part of the agreed schema. You should send email_domain if available and relevant to the configuration.\n ","parameters":[{"in":"header","name":"x-api-key","schema":{"type":"string","description":"API key for authentication"},"required":true,"description":"API key for authentication"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"name":{"type":"string","description":"The company name (required if website is provided and no linkedinUrl)"},"website":{"type":"string","description":"The company website (required if name is provided and no linkedinUrl)"},"linkedinUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The LinkedIn company profile URL"},"force":{"description":"When true, bypasses the previously enriched check and starts a fresh enrichment.","type":"boolean"},"salesforceId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Salesforce record ID (e.g., Lead IDs often start with 00Q, Account IDs with 001)"}},"required":["salesforceId"]},{"type":"object","properties":{"name":{"type":"string","description":"The company name (required if website is provided and no linkedinUrl)"},"website":{"type":"string","description":"The company website (required if name is provided and no linkedinUrl)"},"linkedinUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The LinkedIn company profile URL"},"force":{"description":"When true, bypasses the previously enriched check and starts a fresh enrichment.","type":"boolean"},"dynamicsId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Dynamics 365 record ID (GUID format)"}},"required":["dynamicsId"]},{"type":"object","properties":{"name":{"type":"string","description":"The company name (required if website is provided and no linkedinUrl)"},"website":{"type":"string","description":"The company website (required if name is provided and no linkedinUrl)"},"linkedinUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The LinkedIn company profile URL"},"force":{"description":"When true, bypasses the previously enriched check and starts a fresh enrichment.","type":"boolean"}},"required":["website"]}],"description":"Request body for initiating inbound enrichment"}}}},"responses":{"200":{"description":"Note the executionId. You will need this unique identifier to poll for results.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"anyOf":[{"type":"string","const":"starting","description":"New enrichment workflow has been initiated"},{"type":"string","const":"cached","description":"Account was recently enriched (within 14 days)"},{"type":"string","const":"in_progress","description":"Enrichment is in progress"}]},"executionId":{"type":"number","description":"Unique identifier for this enrichment job. Use this to poll for results."},"message":{"description":"Optional message providing additional context","type":"string"}},"required":["status","executionId"],"additionalProperties":false,"description":"Successful response from POST enrichment endpoint"}}}},"400":{"description":"400 Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"error","description":"Indicates the request failed"},"message":{"type":"string","description":"Error message explaining what went wrong"}},"required":["status","message"],"additionalProperties":false,"description":"Error response from POST enrichment endpoint"}}}}}}}}}
```

## Polling for Enrichment Status and Results

> Because enrichment happens asynchronously, you need to periodically check the status of your request using the executionId.

```json
{"openapi":"3.1.0","info":{"title":"Kernel Inbound API","version":"1.0.0"},"paths":{"/api/v1/inbound/enrichment":{"get":{"tags":["Enrichment"],"summary":"Polling for Enrichment Status and Results","description":"Because enrichment happens asynchronously, you need to periodically check the status of your request using the executionId.","parameters":[{"in":"header","name":"x-api-key","schema":{"type":"string","description":"API key for authentication"},"required":true,"description":"API key for authentication"},{"in":"query","name":"executionId","schema":{"type":"number","description":"The executionId returned from the POST /enrichment request"},"required":true,"description":"The executionId returned from the POST /enrichment request"}],"responses":{"200":{"description":"Enrichment status and results. The response includes dynamic fields based on your client configuration.","content":{"application/json":{"schema":{"anyOf":[{"allOf":[{"type":"object","properties":{"Id":{"type":"string","description":"Salesforce ID of the record"},"message":{"type":"string","description":"Status message describing the enrichment result"},"status":{"type":"string","const":"complete","description":"All enrichment data is available"}},"required":["Id","message","status"],"additionalProperties":false},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"Completed response from GET enrichment endpoint. Contains Id (Salesforce ID), status, message, and dynamic fields based on client configuration"},{"allOf":[{"type":"object","properties":{"Id":{"type":"string","description":"Salesforce ID of the record"},"message":{"type":"string","description":"Status message describing the current processing state"},"status":{"anyOf":[{"type":"string","const":"processing"},{"type":"string","const":"fast-success"}],"description":"processing: Enrichment is still running. fast-success: Fast data points are available, but enrichment may still be processing slower data points"}},"required":["Id","message","status"],"additionalProperties":false},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"In-progress response from GET enrichment endpoint. Contains Id (Salesforce ID), status, message, and dynamic fields based on client configuration"}]}}}},"400":{"description":"400 Bad Request","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"message":{"type":"string","description":"Error message explaining what went wrong"},"status":{"anyOf":[{"type":"string","const":"error"}],"description":"error: Unrecoverable error occurred."}},"required":["message","status"],"additionalProperties":false},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"Error response from GET enrichment endpoint"}}}},"500":{"description":"500 Internal Server Error","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"message":{"type":"string","description":"Error message explaining what went wrong"},"status":{"anyOf":[{"type":"string","const":"error"}],"description":"error: Unrecoverable error occurred."}},"required":["message","status"],"additionalProperties":false},{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}],"description":"Error response from GET enrichment endpoint"}}}}}}}}}
```

#### Scheduled inbound from Salesforce

In addition to just‑in‑time enrichments that you trigger directly via the API, Kernel can run the inbound process on a **schedule** using our existing Salesforce integration.

* Kernel **polls your Salesforce org** on a defined cadence (for example, every minute, hourly, or daily) using the authorized integration user.
* We **identify new (or updated) records** on a configured object (for example, Account, Lead) that match your agreed criteria for enrichment (such as record type, lifecycle stage, region, or other custom filters).
* For each matching record, Kernel **initiates an enrichment workflow**.
* Once enrichment is complete, Kernel **writes the enriched fields back to those Salesforce records** via the standard integration.

This pattern is useful when you want continuous, automatic enrichment of data in Salesforce without having to build or operate your own orchestration layer against the Inbound API. It works on **any standard or custom object in your CRM** that is included in your Kernel Salesforce configuration.

{% hint style="info" %}
This scheduled inbound process requires **no additional setup or access beyond the existing Salesforce integration**. Kernel reuses the same Connected App, integration user, and permission set described in the [Salesforce integration](https://docs.kernel.ai/integrations/salesforce-integration), including any standard or custom objects you have enabled for Kernel.
{% endhint %}
