SSmartLite Academy
0 pts
Trail MapTrailsIndustriesUse CasesComponents
Appearance
Your points 0 pts

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.

⏱ ~14 min to complete80 pts available🏅 Badge: Integration Engineer

About & Business Case #

Why integrations matter #

FN
Finn Osei · Finance Operations Lead

"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:

DirectionToolUse case
OutboundAPI 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
OutboundConnected Apps — a third-party app that a user approves on a consent screen (OAuth), without copying any keyLet a ready-made app (a marketplace tool, a partner's product) work with your data as the user who approved it
OutboundWebhooks — a signed, verified message, e.g. sent when a record is updatedPush a real-time alert to any web address the moment a record changes
InboundPull Connectors — SmartLite checks another system on a regular scheduleFetch records from another system automatically and add or update them in SmartLite
InboundInbound Webhooks (verified messages) / Bulk & Excel importReceive 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 #

Create an API keyGo to Integrations → API Keys → Generate Key. Name it "Finance Sync," tick the Read and Write scopes, and keep the default expiry of 1 year. Admins can also pick an Owner: choose a dedicated integration user whose profile sees only what this system needs, and the key acts as that user. Copy the key shown — it's only shown once, so save it somewhere safe.
Register an outbound webhookGo to Integrations → Webhooks → New Endpoint. Enter the URL of your test receiver, and pick the event record.updated (fires whenever a record changes).
Trigger itUpdate a real Opportunity's stage to "Closed Won" and check that your receiver got the message.
Verify the signatureCheck the 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.
Test the API keyFrom a terminal, run: 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 #

Register the app (admin)Go to Integrations → Connected Apps → Register App. Enter its name, the redirect URI the app's developer gives you, and the scopes it may ask for. Copy the Client ID and Client secret into the app's settings — the secret is shown only once.
Approve it (each user)When a user clicks "Connect to SmartLite" in the app, they see a SmartLite consent screen listing what the app wants to do. Allow sends them back to the app, which can now work as that user.
Review and revokeIntegrations → Connected Apps lists every app each user has approved. Revoke one to cut it off at once; deactivating the app revokes it for everyone.

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.

Create a read-only keyIntegrations → API Keys → Generate Key. Tick only Read and keep the 1-year expiry. Copy the key.
Ask who you arecurl -H "Authorization: Bearer KEY" https://api.smartlite.app/api/v1/me
You should see your username, "scopes":["READ"], the expiry date and your rate limit.
Read a few records, only some fieldscurl -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.
Filter...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".
Check that a read-only key can't writecurl -X POST -H "Authorization: Bearer KEY" -H "Content-Type: application/json" -d "{\"last_name\":\"Test\",\"company\":\"Test\"}" https://api.smartlite.app/api/v1/records/lead
Expect 403 INSUFFICIENT_SCOPE.
Check that keys only work on the public APIcurl -H "X-API-Key: KEY" https://api.smartlite.app/api/records/lead
Expect 401 API_KEY_NOT_ALLOWED_HERE: a key can never reach SmartLite's own screens and settings.
Get only what changedcurl -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.
Check the key follows its user's permissionsSign in as a user whose profile hides a field (say, Phone on Lead), create a key as them and read leads again: the hidden field isn't there, and an object they can't open gives 403 or 404. Then deactivate that user in Setup → Users: their key now answers 401, and stays revoked even if you re-activate them.
Write, and find it in the audit logCreate a second key with Read and Write and repeat the POST above: 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.
Rotate and revokeClick ↻ next to a key: you get a new key, and the old one keeps working for 24 hours. Click the bin to revoke a key: it answers 401 straight away.
Read one lead by its idcurl -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.
Browse the full referenceEvery endpoint, with examples, is at 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.

Register the appIntegrations → Connected Apps → Register App. Name: "Postman test". Redirect URI: https://oauth.pstmn.io/v1/callback. Scope: Read. Copy the Client ID and Client secret.
Set up PostmanOn a new request, open Authorization → type OAuth 2.0 → Configure New Token:
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.
Approve itClick Get New Access Token. Your browser opens SmartLite's consent screen: "Postman test wants to use SmartLite as you and see records…". Click Allow; Postman receives a token starting slat_.
Use itCall 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.
Take it awayIn Connected Apps, revoke the approval: the same call now answers 401. Clicking Deny on the consent screen instead sends Postman error=access_denied.
Clean upRevoke your test keys, deactivate the "Postman test" app, and delete any test leads.

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…UseScenario
Let another system read or write SmartLite recordsAPI key + /api/v1A
Be told the moment a record changesOutbound webhookB
Let another system push data in, with no code on its sideInbound webhook with a URL tokenC
Accept pushed data only when it is signedInbound webhook with a signatureD
Fetch data from another system on a schedulePull connectorE
Send a lead out to a partnerLead Posting TemplateF

A Read and update records from another system #

Create the keyGo to Integrations → API Keys → Generate Key. Name the key after the system that will use it, e.g. "ERP sync". Tick only the scopes it needs: Read to fetch records, Write to create or update them. Admins can also pick an Owner, and the key then sees exactly what that user sees. Copy the key right away, because it is shown only once.
List recordsEvery request sends the key in the header Authorization: Bearer KEY.
Read one record by its idTake the id from the list. Ids look like LEA000002000000QiHAA; a plain number is refused.
Update itSend only the fields you want to change, using their API names. Custom field names end in __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) #

Get a receiving addressThe other system gives you a public https:// web address to send messages to. Addresses inside a private network, such as http://10.0.0.5 or localhost, are refused.
Add the webhookGo to Integrations → Webhooks → Add Webhook. Enter a name and the address, tick the events you want (Record Created, Updated or Deleted), and type a secret of your own choosing. Save, then give the same secret to the receiving system.
Change a recordCreate or edit a record. Within seconds, SmartLite sends a POST like the example below.
Check the signature on the receiving sideThe receiving system works out the signature itself from the raw message body and the secret, and compares it with the X-Signature-256 header. If they don't match, it should reject the message.
Fetch the full record if neededThe message's recordId is the same id the API uses, so the receiver can call GET /api/v1/records/opportunity/OPP000002000000Rk2AA (scenario A).
Watch the deliveriesThe webhook's delivery history shows every send. A failed send is tried again after 1, 5 and 30 minutes.
# 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) #

Create the sourceGo to Integrations → Data Ingestion → Inbound Webhooks → New. Name: "Shop orders". Slug: shop-orders. Target Object API Name: lead.
Choose what happens to each messageIngestion Mode CREATE adds a new record for every message. UPSERT updates the record whose Match Field has the same value, or creates one if there is none. The Match Field must be marked as an External ID (Object Manager → the field → External ID).
Leave Signature Algorithm at NONE and saveThe source is then protected by a secret token in its address.
Copy the addressClick Copy URL. The address looks like 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.
Send a test messageUse the field API names as the JSON keys. SmartLite answers {"received":true}.
Check the resultOpen the source's logs. Each message shows what arrived and the record it created, or why it failed (for example, a missing required field). A message can be at most 1 MB.
If the address leaksClick Replace URL Token. The old address stops working at once, so give the sender the new one.
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_LARGE

If 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) #

Find the sender's signing secretIt's in the sending system's webhook settings (Stripe calls it the "Signing secret").
Set up the sourceFollow scenario C, but set Signature Algorithm to HMAC_SHA256 (or HMAC_SHA1 if the sender uses that). For Signature Header, enter the header name the sender uses, e.g. 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.
Give the sender the addressClick Copy URL as before. For a signed source, the signature is what gets checked, and the token in the address does no harm.
TestUse the sender's "Send test event" button, or sign a message yourself as in the example below. A wrong signature gets 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) #

Create the connectorGo to Integrations → Data Ingestion → Pull Connectors → New. Name: "Partner contacts". Target Object API Name: contact.
Point it at the other systemEndpoint URL: https://api.partner.com/contacts. Only public addresses work, and redirects are not followed. HTTP Method: GET.
Say where the list of records isIf the answer looks like {"data":[{…},{…}]}, set Response Array Path to $.data.
Avoid duplicatesSet Ingestion Mode to UPSERT and Match Field to email (an External ID field). A contact that arrives twice is then updated instead of copied.
Set the schedulePT15M means every 15 minutes and PT1H means every hour. Save.
Add credentials if neededPick an Auth Type (API_KEY, BEARER, BASIC…) and add the credentials as the form explains. They are stored encrypted and never shown back.
Check the Jobs MonitorEach run shows how many rows were created, updated or failed, and why any failed.

F Send a lead to a partner with their API key (Lead Posting) #

Create the templateGo to Lead Posting Templates → New Template. Posting URL: the partner's public https:// address. Map your fields to the field names the partner expects.
Add their key as a headerEnter it under Custom HTTP Headers, written as JSON (see the example below), and save. When you open the template again, the values show as ••••••. Leave them as they are to keep the key, or type a new one to replace it.
Post a leadOpen a lead and post it with the template. Only the fields you can see are sent.
Check the resultThe lead's posting log shows the partner's answer.
{
  "Authorization": "Bearer partner-key-123",
  "X-Source": "SmartLite"
}

When something goes wrong #

AnswerUsually meansFix
401 on /api/v1The key is missing, wrong, revoked or expired, or its user is deactivatedCheck the Authorization: Bearer header; create a new key or rotate the old one
403 INSUFFICIENT_SCOPEThe key doesn't have the scope it needs (e.g. Write)Create a key with that scope
403 / 404 on a recordThe key's user can't see that object or record, or the id was a plain numberUse 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 userCheck the name in Object Manager, and the user's field access
404 on /api/inboundThe slug is wrong, or the token is missing or out of dateClick Copy URL again; the token may have been replaced
401 on /api/inboundThe signature is missing or wrong, the secret is wrong, or the Stripe timestamp is more than 5 minutes offRe-enter the signing secret, and check the Signature Header name
413 PAYLOAD_TOO_LARGEThe body is over 1 MB (inbound webhook) or 10 MB (API)Send smaller batches
429The rate limit was reachedWait the number of seconds in Retry-After, then try again
Webhook "Not sent: …"The address points into a private networkUse 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 #

Add a new outbound connection when a system needs to knowRegister another webhook whenever a new downstream system needs real-time updates.
Check the delivery log when something looks offOpen Integrations → Webhooks to see the delivery/error history for troubleshooting.

Day-to-day: developers wiring up a new tool #

Request a scoped API keyAsk an admin for a key with only the permissions your integration actually needs — READ-only if you're not writing data back.
Handle rate limits gracefullyWatch for a 429 response and respect the Retry-After header instead of hammering the API.

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.

Quiz #

Test what you learned #

1. A record created through a bulk API import — does it trigger Workflow Rules the same way a manual create does?
Yes — it's treated exactly the same as a record entered by hand
No — imported records skip workflows to save time
2. What happens when an API key goes over its rate limit?
The request quietly succeeds but gets queued up
You get an error code 429 back, with a Retry-After header