Admin & Extend · Module 11
Integrations
Keep Every System of Record in Sync
How API keys and webhooks let SmartLite talk automatically to the other tools your company uses — sending data out, and receiving data in. Some of this trail (testing with a terminal command) is a bit more technical than most.
About & Business Case #
Why integrations matter #
"The moment a deal closes, our finance system needs to know immediately so invoicing can start. Nobody should have to manually retype closed deals into a second system — that's exactly how numbers drift apart."
Almost every business runs more than one software system — a CRM, a finance tool, a support tool, and so on. An integration is a connection that lets two systems talk to each other automatically, without a person copying data between them by hand.
What this means for you
Closed deals reach other systems the instant they close. Data from outside systems lands in the CRM automatically too — with a full log of what was sent and received, for troubleshooting.
Implementation #
Anatomy of the integration layer #
Data can flow two ways: outbound (SmartLite sending data out to another system) and inbound (another system sending data into SmartLite). Each direction has its own tools:
| Direction | Tool | Use case |
|---|---|---|
| Outbound | API Keys — a secret password-like code that acts as its owner (the user who created it, or an integration user an admin picks), narrowed further by scopes (READ / WRITE / DELETE / BULK / ADMIN) | Let an external system securely ask SmartLite for data or send it changes |
| Outbound | Connected Apps — a third-party app that a user approves on a consent screen (OAuth), without copying any key | Let a ready-made app (a marketplace tool, a partner's product) work with your data as the user who approved it |
| Outbound | Webhooks — a signed, verified message, e.g. sent when a record is updated | Push a real-time alert to any web address the moment a record changes |
| Inbound | Pull Connectors — SmartLite checks another system on a regular schedule | Fetch records from another system automatically and add or update them in SmartLite |
| Inbound | Inbound Webhooks (verified messages) / Bulk & Excel import | Receive data pushed from elsewhere — like a new order from an online store — and create or update CRM records automatically |
Same rules as manual entry
Every record that arrives this way is treated exactly like one entered by hand. Workflows, audit logs (the permanent change history), plan limits, and outbound webhooks all still apply.
A key can never do more than its owner
An API key works as the user who created it: it sees only the records, objects and fields that user can see, and it stops working the moment that user is deactivated. Scopes can only narrow it further. Non-admins can create read-only keys; ask an admin for anything more. Keys work only on the public API (/api/v1), expire after a year by default, and can be rotated (Integrations → API Keys → the ↻ button): you get a new key and the old one keeps working for 24 hours while you switch over.
Keeping incoming connections safe
Integrations → Data Ingestion → Inbound Webhooks: every inbound webhook has its own secret web address. Copy URL copies it with its token; treat it like a password. If it leaks, click Replace URL Token (the senders then need the new address). A source set to check signatures needs its signing secret, which is stored encrypted and never shown again. Inbound webhooks, pull connectors, file drops and the Jobs Monitor are admin-only. Each message can be at most 1 MB (10 MB on /api/v1), and an Excel import at most 10 MB and 50,000 rows.
Rate limits are real
A rate limit caps how many requests you can make per minute — think of it as a speed limit for API traffic. Each API key gets 60 requests per minute on the Starter plan, more on paid plans. Go over it, and you get an error code 429 back, with a Retry-After header telling you how long to wait.
1 Push closed deals to a finance webhook #
record.updated (fires whenever a record changes).X-Signature-256 header — a code that proves the message really came from SmartLite. Recompute it using your endpoint secret to confirm it matches. A webhook address must be a public https:// address: SmartLite refuses addresses inside a private network, and skips a send if the address later points to one.curl -H "Authorization: Bearer <key>" https://api.smartlite.app/api/v1/records/opportunity and confirm you get your records back.2 Connect a third-party app with OAuth #
3 Test your API key, step by step #
Each check below takes one command in a terminal (Command Prompt, PowerShell or Terminal on a Mac all work). Replace KEY with your key. Use a test org, or delete what you create afterwards.
curl -H "Authorization: Bearer KEY" https://api.smartlite.app/api/v1/meYou should see your username,
"scopes":["READ"], the expiry date and your rate limit.curl -H "Authorization: Bearer KEY" "https://api.smartlite.app/api/v1/records/lead?size=5&fields=first_name,last_name,email"Up to five leads with just those fields. For the next five, add
&cursor= with the nextCursor value from the answer....api/v1/records/lead?filter=company:contains:acme returns only matching leads. A field that doesn't exist, or one you're not allowed to see, gives 400 "Unknown field".curl -X POST -H "Authorization: Bearer KEY" -H "Content-Type: application/json" -d "{\"last_name\":\"Test\",\"company\":\"Test\"}" https://api.smartlite.app/api/v1/records/leadExpect
403 INSUFFICIENT_SCOPE.curl -H "X-API-Key: KEY" https://api.smartlite.app/api/records/leadExpect
401 API_KEY_NOT_ALLOWED_HERE: a key can never reach SmartLite's own screens and settings.curl -H "Authorization: Bearer KEY" https://api.smartlite.app/api/v1/records/lead/sync returns records and a nextChangedCursor. Edit a lead in SmartLite, call again with ?sinceChanged= and that cursor: only the lead you edited comes back.403 or 404. Then deactivate that user in Setup → Users: their key now answers 401, and stays revoked even if you re-activate them.201 and a new lead. In Setup → Audit Log, its entry ends with "via API key" and the key's name, so you always know which integration changed what.401 straight away.curl -H "Authorization: Bearer KEY" https://api.smartlite.app/api/v1/records/lead/ID, using the id from the list above. The answer has id, object and the field values under record.https://api.smartlite.app/swagger-ui.html.4 Test a connected app with Postman #
Postman (a free tool for trying out APIs) can play the part of a third-party app, so you can see the whole approval flow.
https://oauth.pstmn.io/v1/callback. Scope: Read. Copy the Client ID and Client secret.Grant type: Authorization Code (With PKCE) · Callback URL:
https://oauth.pstmn.io/v1/callback (tick "Authorize using browser") · Auth URL: https://app.smartlite.app/oauth/authorize · Access Token URL: https://api.smartlite.app/api/v1/oauth/token · Client ID and Secret from the step above · Code Challenge Method: SHA-256 · Scope: READ · Client Authentication: Send as Basic Auth header.slat_.GET https://api.smartlite.app/api/v1/me with the token: "credentialType":"OAUTH" and the app's name. A POST to create a lead gives 403, because you approved Read only.401. Clicking Deny on the consent screen instead sends Postman error=access_denied.Step by step: every integration scenario #
Find the row that matches what you want to do, then follow that scenario's steps. Each scenario ends with an example you can copy. Replace KEY, ORG, TOKEN and SECRET with your own values.
| I want to… | Use | Scenario |
|---|---|---|
| Let another system read or write SmartLite records | API key + /api/v1 | A |
| Be told the moment a record changes | Outbound webhook | B |
| Let another system push data in, with no code on its side | Inbound webhook with a URL token | C |
| Accept pushed data only when it is signed | Inbound webhook with a signature | D |
| Fetch data from another system on a schedule | Pull connector | E |
| Send a lead out to a partner | Lead Posting Template | F |
A Read and update records from another system #
Authorization: Bearer KEY.id from the list. Ids look like LEA000002000000QiHAA; a plain number is refused.__c.# List five leads curl -H "Authorization: Bearer KEY" "https://api.smartlite.app/api/v1/records/lead?size=5" # Read one lead curl -H "Authorization: Bearer KEY" https://api.smartlite.app/api/v1/records/lead/LEA000002000000QiHAA # The answer { "success": true, "data": { "id": "LEA000002000000QiHAA", "object": "lead", "objectLabel": "Lead", "record": { "first_name": "Asha", "last_name": "Rao", "company": "Acme", "email": "asha@acme.com", "credit_score__c": 720 }, "createdBy": "admin", "createdAt": "2026-10-20T09:15:00" }, "meta": { "requestId": "5043ef8edcb54dbb" } } # Update it (needs the Write scope) curl -X PATCH -H "Authorization: Bearer KEY" -H "Content-Type: application/json" \ -d "{\"status\":\"Contacted\"}" https://api.smartlite.app/api/v1/records/lead/LEA000002000000QiHAA
Field values always come back under record. Fields the key's user isn't allowed to see are left out. A request body can be at most 10 MB.
B Get told when a record changes (outbound webhook) #
https:// web address to send messages to. Addresses inside a private network, such as http://10.0.0.5 or localhost, are refused.X-Signature-256 header. If they don't match, it should reject the message.recordId is the same id the API uses, so the receiver can call GET /api/v1/records/opportunity/OPP000002000000Rk2AA (scenario A).# What SmartLite sends POST https://hooks.example.com/smartlite X-Signature-256: sha256=9f2c…e81a { "event": "record.updated", "objectApiName": "opportunity", "recordId": "OPP000002000000Rk2AA", "actorUsername": "john", "data": { "name": "Acme renewal", "stage": "Closed Won" } } # Checking it on the receiving side (Python) import hmac, hashlib expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, request.headers["X-Signature-256"]): reject()
C Receive data pushed from another system (inbound webhook) #
shop-orders. Target Object API Name: lead.https://api.smartlite.app/api/inbound/ORG/shop-orders?token=TOKEN. Give it only to the sending system, because anyone who has it can add records.{"received":true}.curl -X POST "https://api.smartlite.app/api/inbound/ORG/shop-orders?token=TOKEN" \
-H "Content-Type: application/json" \
-d "{\"first_name\":\"Asha\",\"last_name\":\"Rao\",\"company\":\"Acme\",\"email\":\"asha@acme.com\"}"
# Answer
{ "received": true, "source": "shop-orders" }
# Wrong or missing token → 404 "Webhook source not found" (on purpose: it doesn't reveal the source exists)
# Source switched off → 410 "Webhook source is disabled"
# Body over 1 MB → 413 PAYLOAD_TOO_LARGEIf the sending system can't add ?token= to the address, it can send the token in an X-Webhook-Token header instead.
D Accept only signed messages (Stripe, GitHub, Shopify and similar) #
Stripe-Signature or X-Hub-Signature-256. Paste the secret into Signature Secret. It is required, stored encrypted and never shown again; when you edit the source later, leave it blank to keep it.401 and is logged.# Both header formats are accepted: # GitHub style: sha256=<hex> (signs the body) # Stripe style: t=<unix time>,v1=<hex> (signs "time.body"; must be within 5 minutes) # Sign a test message yourself (GitHub style, bash) BODY='{"first_name":"Asha","last_name":"Rao","company":"Acme"}' SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "SECRET" | sed 's/^.* //') curl -X POST "https://api.smartlite.app/api/inbound/ORG/shop-orders" \ -H "Content-Type: application/json" -H "X-Hub-Signature-256: sha256=$SIG" -d "$BODY"
E Fetch data from another system on a schedule (pull connector) #
contact.https://api.partner.com/contacts. Only public addresses work, and redirects are not followed. HTTP Method: GET.{"data":[{…},{…}]}, set Response Array Path to $.data.email (an External ID field). A contact that arrives twice is then updated instead of copied.PT15M means every 15 minutes and PT1H means every hour. Save.F Send a lead to a partner with their API key (Lead Posting) #
https:// address. Map your fields to the field names the partner expects.••••••. Leave them as they are to keep the key, or type a new one to replace it.{
"Authorization": "Bearer partner-key-123",
"X-Source": "SmartLite"
}When something goes wrong #
| Answer | Usually means | Fix |
|---|---|---|
401 on /api/v1 | The key is missing, wrong, revoked or expired, or its user is deactivated | Check the Authorization: Bearer header; create a new key or rotate the old one |
403 INSUFFICIENT_SCOPE | The key doesn't have the scope it needs (e.g. Write) | Create a key with that scope |
403 / 404 on a record | The key's user can't see that object or record, or the id was a plain number | Use the text id; check the user's profile and sharing |
400 "Unknown field" / "Field not found" | The API name is wrong, or the field is hidden from the user | Check the name in Object Manager, and the user's field access |
404 on /api/inbound | The slug is wrong, or the token is missing or out of date | Click Copy URL again; the token may have been replaced |
401 on /api/inbound | The signature is missing or wrong, the secret is wrong, or the Stripe timestamp is more than 5 minutes off | Re-enter the signing secret, and check the Signature Header name |
413 PAYLOAD_TOO_LARGE | The body is over 1 MB (inbound webhook) or 10 MB (API) | Send smaller batches |
429 | The rate limit was reached | Wait the number of seconds in Retry-After, then try again |
| Webhook "Not sent: …" | The address points into a private network | Use a public https:// address |
If an answer looks wrong
Every API answer carries a requestId. Send the command you ran and that id to your SmartLite administrator or support, and they can find exactly what happened.
How to Use #
Day-to-day: admins and finance ops #
Day-to-day: developers wiring up a new tool #
What to try next
Look at the Zapier Integration trail — it uses this exact same API key and webhook system, pre-wired for no-code tools.