qls-lime-go-agentic
Python SDK for the Lime Go Agentic public CRM API (JSON:API, OAuth2 client
credentials). Sync and async clients, fully typed request/response models (pydantic v2,
mypy --strict clean), automatic token handling, idempotent retries, ETag concurrency,
pagination and the change feed.
Status: early release. Built against the API's OpenAPI spec v1.0.0, which Lime marks as an unpublished v1 pilot, and verified against the live API on 2026-10-14 (reads, plus task/activity writes). Not an official Lime product. See docs/OPEN_QUESTIONS.md for what was verified and the spec gaps still to confirm with Lime.
Install
pip install qls-lime-go-agentic
The distribution is qls-lime-go-agentic; the import package is lime_go_agentic.
Requires Python 3.10+. Depends on httpx and pydantic>=2.
Quick start
from decimal import Decimal
from lime_go_agentic import LimeGoClient, CompanyCreate, DealCreate, DealUpdate
with LimeGoClient(client_id="...", client_secret="...", environment="staging") as lime:
# Upsert by an id WE own (e.g. the contract number) - safe to re-run.
company = lime.companies.upsert("ORG-5560000000", CompanyCreate(name="Acme AB", status="customer")).resource
deal = lime.deals.upsert(
"AVT-2026-0042",
DealCreate(name="NaaS Kontor", value=Decimal("125000"), currency="SEK", company=company.id),
).resource
# Optimistic concurrency: pass the resource (or its ETag) as if_match.
deal = lime.deals.update(deal.id, DealUpdate(probability=90), if_match=deal)
for c in lime.companies.iter({"status": "customer"}, include=["owner"]):
print(c.id, c.attributes.name, c.relationships.owner.id)
Async: AsyncLimeGoClient has the same surface with await / async for.
Authentication & configuration
POST {base_url}/oauth/token with grant_type=client_credentials. Tokens are opaque,
live 1800 s and have no refresh token; the SDK caches them, refreshes before expiry and
retries once on a 401.
| Setting | Env var (LIME_GO_[<ENV>_]...) |
Notes |
|---|---|---|
client_id |
CLIENT_ID |
required |
client_secret |
CLIENT_SECRET |
required |
environment |
LIME_GO_ENV |
staging is the only known URL |
base_url |
BASE_URL |
required for any other instance |
scopes |
SCOPES (space-separated) |
omit to get the client's default |
There is no default environment: a deploy without LIME_GO_ENV/LIME_GO_BASE_URL
fails fast instead of writing to the wrong instance. Values are looked up
environment-first (LIME_GO_PROD_CLIENT_ID before LIME_GO_CLIENT_ID), so one .env
can hold several instances. client.granted_scopes shows what the server granted.
Scopes: {companies,contacts,deals,activities,tasks}:{read,write,delete},
reference:read (owners, stages, tags, activity types, custom-field definitions,
relationship reads) and changes:read. Request only what an integration needs.
API map
| Namespace | Operations |
|---|---|
companies, contacts, deals, activities, tasks |
list, iter, next_page, get, create, upsert(external_id), update, delete |
companies.tags, contacts.tags |
relationship list, iter, add, remove, replace |
deals.contacts, deals.owners, deals.tags |
same as above |
tasks.companies, tasks.contacts, tasks.deals |
same as above |
owners, stages, tags, activity_types, custom_field_definitions |
list, iter, next_page, get |
changes |
read(cursor), iter_pages(cursor) |
request(...) |
escape hatch for any path |
All 74 operations in the spec are covered; tests/test_spec_conformance.py enforces it.
Writing
Write bodies are typed models: XxxCreate (create + upsert) and XxxUpdate (PATCH).
Only fields you set are sent: omit a field to leave it unchanged, set a nullable field to
None to clear it. Non-nullable fields, unknown fields, bad enum values, >100 custom
fields, >100 relationship ids and out-of-range values fail locally (pydantic) and, with
the pydantic mypy plugin, at type-check time. Relationships are plain ids:
DealCreate(name=..., company="<uuid>", owners=["<uuid>"]).
Notes from the spec:
ActivityUpdateonly acceptscontentandtype; relationships andoccurred_atcan only be set on create. In Python the activitytypeattribute isactivity_type.TaskCreaterequiresowner(an owner id, or explicitlyNone).- Deal
valueis aDecimal, sent as the API's decimal string (max 15+4 digits).
Idempotency
Every write carries an Idempotency-Key (the API requires it). The SDK generates one per
call and reuses it across its own retries, so a retried POST cannot create duplicates.
For retries across processes (a re-run sync job), pass your own stable key:
create(data, idempotency_key="sync-AVT-2026-0042"). Better still, use upsert with an
external id.
Concurrency (ETag / If-Match)
update, delete and relationship writes require if_match. Resources returned by
get/create/update/upsert carry the response .etag; items from a list page derive
it from meta.revision as "<id>:<revision>" (the API's format, verified live), so you can
pass any resource itself. Only resources without a revision raise MissingETagError. A
stale ETag raises PreconditionFailedError (412): re-read
and retry. Relationship calls return the parent's new ETag in page.etag.
Pagination
Cursor-based, max 100 per page. list() returns a Page (data, included,
next_url, has_more); iter() follows links.next for you. include= (CRUD
resources) returns related to-one records typed in page.included. fields= limits the
attributes returned (timestamps are always included). sort is "id" or "-id".
Change feed
checkpoint = lime.changes.read() # first run: get a checkpoint cursor, persist it
for page in lime.changes.iter_pages(cursor):
for ch in page.data:
a = ch.attributes # resource_type, resource_id, revision, deleted
if a.origin_integration_id == MY_INTEGRATION_ID:
continue # skip echoes of our own writes
...
save_cursor(page.cursor) # durable; resume from here next time
The request parameter carrying the cursor is not documented in the spec; the SDK sends
cursor(lime_go_agentic.resources.CHANGES_CURSOR_PARAM). To be confirmed with Lime.
Errors
All errors derive from LimeGoError. HTTP errors raise an APIStatusError subclass per
status (BadRequestError 400, UnauthorizedError 401, ForbiddenError 403,
NotFoundError 404, NotAcceptableError 406, ConflictError 409, GoneError 410,
PreconditionFailedError 412, PayloadTooLargeError 413,
UnsupportedMediaTypeError 415, UnprocessableEntityError 422,
PreconditionRequiredError 428, RateLimitError 429, ServiceUnavailableError 503,
ServerError 5xx) with .errors (parsed JSON:API error objects), .code (Lime
documents codes as stable, e.g. relationship_too_large, unavailable) and
.request_id (x-request-id, quote it to Lime support).
Retries: network errors, 429 and 5xx are retried max_retries times (default 2) with
backoff, honouring Retry-After up to max_retry_after (default 30 s); a longer
server-requested wait raises immediately instead of blocking.
Logging
The SDK logs to the lime_go_agentic logger: DEBUG (method, path, status, timing,
request id) and WARNING (retries). It never logs credentials, tokens, headers or bodies.
Development
uv venv .venv && uv pip install -e ".[dev]"
.venv/bin/mypy src/lime_go_agentic
.venv/bin/pytest -q # unit tests (integration tests deselected)
.venv/bin/pytest -m integration # live, needs LIME_GO_* in the environment
Spec conformance: Lime's OpenAPI spec is not redistributed here. Fetch it with
python scripts/fetch_spec.py (writes the gitignored spec/openapi.json), then run
pytest tests/test_spec_conformance.py; every failure names the exact difference. CI
fetches the live spec on every run, so it also flags when Lime changes the API.
License
MIT
Metadata
Release files for qls-lime-go-agentic 26.10.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qls_lime_go_agentic-26.10.14.tar.gz | 44.7 kB | Without provenance | Details for qls_lime_go_agentic-26.10.14.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qls_lime_go_agentic-26.10.14-py3-none-any.whl | Python 3 | none | any | Details for qls_lime_go_agentic-26.10.14-py3-none-any.whl |
Total release size: 80.0 kB
Release files / qls_lime_go_agentic-26.10.14.tar.gz
| Download URL | qls_lime_go_agentic-26.10.14.tar.gz |
|---|---|
| Size | 44.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
418776c3e2ae7604ea182851e66b58915f6b4ac3b892b143a2c0b7a5e3205dfa
|
|
BLAKE2b-256 checksum How to use checksums |
a23f525b35fae83382e58c6bec30afa2b40240e28b096ff209dba835d9cae5dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / qls_lime_go_agentic-26.10.14-py3-none-any.whl
| Download URL | qls_lime_go_agentic-26.10.14-py3-none-any.whl |
|---|---|
| Size | 35.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f11269653c1fa64ccbd2699a2e7e26de405d5922ed6d860e9aec066f40ba3677
|
|
BLAKE2b-256 checksum How to use checksums |
e141e52fdb75f4a8b6186a13dde44c536baec3922bf89bee64d40f3b65b1cf77
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Security for qls-lime-go-agentic 26.10.14
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.