Skip to main content

LinkedIn settings API

Every /api/v1/linkedin/settings mode for templates, automation, monitoring, the message after acceptance and email, with its query, body, answer, scope and errors

LinkedIn settings live on one path, https://app.makesmores.com/api/v1/linkedin/settings. It holds the workspace's message templates and automation defaults, and each LinkedIn account's automation, monitoring hours, message after acceptance and email. Paths below are relative to https://app.makesmores.com/api/v1. Send Authorization: Bearer <key> on every call and bodies as JSON with Content-Type: application/json.

note

Monitoring is the only section on this page that's enabled in production so far. Until templates, automation, the message after acceptance and email are enabled, their modes answer 404 not_found.

Each mode takes level=workspace or account_id, never both, and a section. account_id is a LinkedIn account's connection_id, as GET /linkedin/accounts lists it. The method and the query together pick one mode, and a request that matches no mode answers 400 invalid_request before S'more reads the key, and costs nothing. A body can't repeat the account: a connection_id in it answers 400 invalid_request. While LinkedIn is closed, every request to this path gets the same plain text 404 page not found as a path S'more doesn't serve. API conventions covers these rules and what every route here shares: errors, caching, billing, timeouts, retries and paging. Every mode bills $0.001 per call.

Answers use snake_case. The JSON samples below are shortened, so read fields by name and expect more of them. No mode here sends a LinkedIn message or an email while you wait. Some writes approve messages that S'more sends later, and each section says which.

How approval works​

Templates hold the exact text S'more sends, and approvals decide when it may send it:

  • There are five template slots: invitation, after_accept for the first message after someone accepts, follow_up, request_email for asking a person who replied for an email address, and email. Each save of a slot adds a numbered revision, and a revision never changes after that. A save approves nothing and changes nothing already waiting to send.
  • The automation defaults pin one revision each for the after_accept, follow_up and request_email steps, with the delays between them. Saving the defaults approves nothing either. The invitation revision is chosen on each list append, and the email revision when the mailbox owner approves email.
  • Activating an account is the approval. When an owner or admin activates an account, with apply or the account write, it takes the defaults at the revision they name, under the caller's own key. Automated messages then go only to people who accept after the activation's cutoff_at, and anyone who accepted earlier is skipped. Another approval while the activation is live keeps its cutoff_at. Approving again after the account was off or the approval lapsed sets a new one.
  • To use a newer template, read behind on the defaults, save the defaults with the new revision and apply them again. Steps S'more hasn't started yet take the new text. A message that's being sent, or was sent, keeps its text. An account with exceptions answers kept_exceptions and keeps its earlier text until you apply with replace_exceptions, or change the account on its own with the new defaults_revision.
  • Exceptions let one account differ from the defaults, such as a longer follow-up delay.
  • The message after acceptance is a separate approval of exact text for one account, with its own revision and its own cut-off. It reaches only people who have no first message of their own and accept after it was turned on. While it's on, it takes the place of the defaults' after_accept step, and first_message_source on the account's automation says which one applies.
  • Email is approved per LinkedIn account by the owner of the mailbox that sends it, and the approval binds one revision of the email template. A later save of that template changes nothing until the mailbox owner approves again. Compare binding.template_revision with binding.current_template_revision on the email section to see whether a newer revision is waiting.
  • No template needs a report, a file or an asset. The email template needs a subject and a body, and a report field in a save answers 400 invalid_request.

Who can change what​

Every write needs an owner or admin. S'more reads the role the key's person holds now, on each call, so an admin whose role was changed to member after the key was made gets 403 admin_required. An approval binds the caller's own key. It lapses when that key is revoked or expires, or when its person is no longer an owner or admin, and nothing it approved sends until someone approves again.

Saving templates and defaults, activating an account and approving the message after acceptance also need admin:write. Workspace admin API says how a key gets it.

ActionScopesAuthority
Read templateslinkedin:activity:readAny member. Members see names and revisions, not text. LinkedIn slot text also needs linkedin:inbox:read on the key
Save a template or the defaultslinkedin:automation:write, admin:writeOwner or admin
Read the defaults or an account's automationlinkedin:activity:readAny member. Members don't see the approver's name or key
Activate, apply or set exceptionslinkedin:automation:write, linkedin:inbox:read, linkedin:outreach:send, admin:writeOwner or admin. Binds the caller's own key
Pause, resume or turn off an accountlinkedin:automation:writeOwner or admin
Read monitoringlinkedin:activity:readAny member
Change monitoringlinkedin:automation:write, linkedin:inbox:readOwner or admin. No test covers the refusal without linkedin:inbox:read yet
Read the message after acceptance or its suggestionslinkedin:inbox:readOwner or admin
Approve the message after acceptancelinkedin:automation:write, linkedin:inbox:read, linkedin:outreach:send, admin:writeOwner or admin. Binds the caller's own key
Turn the message after acceptance offlinkedin:automation:write, linkedin:inbox:read, linkedin:outreach:sendOwner or admin
Read emailgmail:readAny member. Members don't see the template copy, the sending address or the pairings
Ask to pair a mailboxgmail:writeThe mailbox's owner, who is an owner or admin. Add gmail:read to see pairings in the answer
Confirm a pairinggmail:writeThe workspace owner
Remove a pairinggmail:writeThe workspace owner, or the mailbox's owner who is an admin
Approve emailgmail:sendThe owner of the confirmed mailbox, who is an owner or admin. Binds the caller's own key. Add gmail:read to read the answer
Turn email offgmail:writeAny owner or admin

Templates​

note

Templates aren't enabled in production yet. Until they are, every section=templates mode answers 404 not_found.

Method and queryScopeAuthorityDoes
GET /linkedin/settings?level=workspace&section=templateslinkedin:activity:readAny memberThe current revision of each saved slot
GET ...&section=templates&name=linkedin:activity:readAny memberOne slot's current revision
GET ...&section=templates&name=&revision=linkedin:activity:readAny memberOne exact revision
PATCH /linkedin/settings?level=workspace&section=templates&name=linkedin:automation:write, admin:writeOwner or adminSaves the slot's next revision. It approves nothing

name is invitation, after_accept, follow_up, request_email or email, and revision needs a name. A read answers its revisions in items, in slot order, with no paging. A slot that was never saved isn't listed, and a revision that doesn't exist answers 404 not_found.

{
"items": [
{
"name": "email",
"revision": 1,
"created_at": "2026-10-30T12:00:00Z",
"created_by": "33333333-3333-4333-8333-333333333333",
"created_by_name": "Alex Kim",
"subject": "Great to connect, {first_name}",
"body": "Hi {first_name},\n\nThanks for sharing your email.\n\n{signature}",
"calendar_url": "",
"signature": "Alex Kim"
}
]
}

A LinkedIn slot carries text, and the email slot carries subject, body, calendar_url and signature. A member gets the same items with the text fields empty. An owner or admin reads the email slot, and reads the LinkedIn slots only when the key also holds linkedin:inbox:read.

Save a LinkedIn slot​

PATCH ...&section=templates&name=follow_up, and the same for invitation, after_accept and request_email, takes:

FieldRules
expected_revisionRequired. The slot's current revision, or 0 for its first save
textRequired. The exact message, up to 1,000 characters counted in UTF-16 code units. An invitation note is held to the workspace's invitation_note_limit, which the monitoring section reports

S'more sends LinkedIn text exactly as written, so a slot takes no merge fields. A {word} token in braces, such as {first_name}, answers 400 invalid_request with field merge_field:

{ "code": "invalid_request", "message": "Invalid request. Field: merge_field.", "runsRemaining": 0, "field": "merge_field" }

A blank text, or one over the limit in UTF-16 code units, names field text, or invitation_note on the invitation slot. Text that's empty or over 1,000 characters answers 400 invalid_request without a field. The answer is the new revision:

{
"name": "follow_up",
"revision": 1,
"created_at": "2026-10-30T12:00:00Z",
"created_by": "33333333-3333-4333-8333-333333333333",
"created_by_name": "Alex Kim",
"text": "Following up on my note."
}

Save the email template​

PATCH ...&section=templates&name=email takes:

FieldRules
expected_revisionRequired. As for a LinkedIn slot
subjectRequired. Up to 200 characters on one line. It needs words besides {first_name}, because the name can be missing
bodyRequired. Up to 5,000 characters
calendar_urlOptional. An https:// link of up to 500 characters. Required only when the body uses {calendar_link}
signatureOptional. Up to 1,000 characters

An empty subject or body, or a value longer than the table allows, answers 400 invalid_request without a field.

The email takes three merge fields:

  • {first_name} only in the subject, or in a greeting line on its own such as Hi {first_name},. Without a name, the subject drops it and the greeting reads Hi there,.
  • {calendar_link} only in the body, where it becomes calendar_url.
  • {signature} only in the body. Without it, a signature goes after the body.

Any other {word} token, or one of these in the wrong place, is refused. A refusal answers 400 invalid_email with field naming the part at fault, such as calendar_url, first_name, merge_field or subject_without_name:

{ "code": "invalid_email", "message": "Add a calendar link that starts with https://. Field: calendar_url.", "runsRemaining": 0, "field": "calendar_url" }

S'more trims the edges of each field and uses plain line breaks, and the answer shows the copy as saved. A field this page doesn't list, such as a report link, answers 400 invalid_request.

A save never changes an approved email. The mailbox owner approves the new revision in Email before S'more sends it.

Automation defaults​

note

Automation defaults aren't enabled in production yet. Until they are, every level=workspace&section=automation mode, apply included, answers 404 not_found.

Method and queryScopeAuthorityDoes
GET /linkedin/settings?level=workspace&section=automationlinkedin:activity:readAny memberThe workspace's defaults
PATCH /linkedin/settings?level=workspace&section=automationlinkedin:automation:write, admin:writeOwner or adminReplaces the defaults. It approves nothing
PATCH ...&section=automation&action=applylinkedin:automation:write, linkedin:inbox:read, linkedin:outreach:send, admin:writeOwner or adminActivates the defaults on up to 25 accounts under the caller's key

The defaults read:

{
"revision": 1,
"after_accept_delay_seconds": 0,
"follow_up_delay_seconds": 259200,
"steps": ["after_accept", "follow_up"],
"templates": { "after_accept": 1, "follow_up": 1, "request_email": null },
"updated_by": "33333333-3333-4333-8333-333333333333",
"updated_by_name": "Alex Kim",
"updated_at": "2026-10-30T12:00:00Z",
"current_template_revisions": { "after_accept": 2, "follow_up": 1, "request_email": null },
"behind": ["after_accept"]
}

Before the first save, revision is 0 and steps is empty. behind names each step whose pinned revision is older than that slot's latest save.

Save the defaults​

The PATCH replaces the whole record, so send every field:

FieldRules
expected_revisionRequired. The defaults' revision, or 0 for the first save
after_accept_delay_secondsRequired. How long after an acceptance the first message waits, 0 to 7,776,000 (90 days)
follow_up_delay_secondsRequired. How long after the first message the follow-up waits, 3,600 (one hour) to 7,776,000
stepsRequired. Which of after_accept, follow_up and request_email run, each at most once. [] runs none
templatesRequired. An object with after_accept, follow_up and request_email, each a saved revision of that slot or null. Each step in steps needs one

A revision that wasn't saved answers 400 invalid_request with field such as templates.follow_up. While email requests are closed, request_email in steps answers 400 invalid_request with field steps. Saving the same values again keeps the revision. The answer is the defaults as GET reads them.

Apply the defaults​

PATCH ...&section=automation&action=apply takes:

FieldRules
defaults_revisionRequired. The defaults' current revision
account_idsRequired. 1 to 25 different account ids
replace_exceptionsOptional. true clears each account's exceptions and applies the defaults. Without it, an account with exceptions is left as it is and answers kept_exceptions

A stale defaults_revision answers 409 revision_conflict and changes nothing. Otherwise each account is written on its own, and the answer has one result per account in the order you sent them:

{
"defaults_revision": 1,
"results": [
{
"account_id": "55555555-5555-4555-8555-555555555555",
"sender_urn": "urn:li:fsd_profile:alex-kim",
"result": "activated",
"revision": 1,
"email": { "adoption": "needs_pairing", "mailbox_id": null, "template_revision": 1 }
},
{
"account_id": "66666666-6666-4666-8666-666666666666",
"sender_urn": "urn:li:fsd_profile:jo-park",
"result": "needs_monitoring_policy",
"reasons": ["monitoring_off", "account_campaign_missing"],
"revision": 0,
"email": { "adoption": "mailbox_not_connected", "mailbox_id": null, "template_revision": null }
}
]
}
ResultMeaning
activatedThe account now runs these defaults under your key, with a new cutoff_at
updatedThe account was already active, and now runs these defaults under your key. Its cutoff_at stays
pausedThe account is paused. It now holds these defaults and stays paused
unchangedThe account already ran exactly this, approved with your key. Nothing changed
kept_exceptionsThe account has exceptions, so nothing changed for it: it keeps its status, its revision and the defaults it ran before. Apply again with replace_exceptions true, or change the account on its own
needs_monitoring_policyThe account can't run yet. reasons says why
needs_authorityYour role no longer allows the approval. Nothing changed
not_foundNo LinkedIn account with that id in this workspace
conflictThat account couldn't be written this time. Nothing changed for it, so apply again

revision is the account's automation revision after the call, and email is how far its email setup has got, as in Account automation. Applying again with the same key is safe: an account already running these defaults under your key answers unchanged. An apply with another key answers updated and binds the account to that key.

If the defaults run request_email while email requests are closed, apply answers 409 automation_not_ready with reasons ["email_request_unavailable"] and changes nothing.

Account automation​

note

Account automation isn't enabled in production yet. Until it is, section=automation on an account answers 404 not_found, to read and to change.

Method and queryScopeAuthorityDoes
GET /linkedin/settings?account_id=&section=automationlinkedin:activity:readAny memberThe account's automation as it runs now
PATCH /linkedin/settings?account_id=&section=automationlinkedin:automation:write. Activating and exceptions also need linkedin:inbox:read, linkedin:outreach:send and admin:writeOwner or adminActivates, pauses, resumes or turns off the account's automation, or sets its exceptions
{
"account_id": "55555555-5555-4555-8555-555555555555",
"sender_urn": "urn:li:fsd_profile:alex-kim",
"account_campaign_id": "77777777-7777-4777-8777-777777777777",
"status": "active",
"revision": 1,
"inherits": true,
"exceptions": null,
"defaults_revision": 1,
"defaults_current_revision": 1,
"after_accept_delay_seconds": 0,
"follow_up_delay_seconds": 259200,
"steps": ["after_accept", "follow_up"],
"templates": { "after_accept": 1, "follow_up": 1, "request_email": null },
"cutoff_at": "2026-10-30T12:00:00Z",
"first_message_source": "workspace_after_accept",
"approval": {
"actor_id": "33333333-3333-4333-8333-333333333333",
"actor_name": "Alex Kim",
"grant_id": "99999999-9999-4999-8999-999999999999",
"grant_label": "Setup agent",
"approved_at": "2026-10-30T12:00:00Z",
"expires_at": "2027-01-28T12:00:00Z",
"lapsed_at": null
},
"readiness": { "ready": true, "reasons": [] },
"email": { "adoption": "needs_mailbox_owner", "mailbox_id": "44444444-4444-4444-8444-444444444444", "template_revision": 1 }
}
  • status is off, active, paused or lapsed. An account that was never activated reads off with revision 0. lapsed means the approving key or role no longer allows sending.
  • defaults_revision is the defaults the account runs, and defaults_current_revision the latest. inherits is false when the account has exceptions.
  • approval.expires_at is when the approving key runs out, so you can approve again before the account lapses. A member gets approval with actor_name, grant_id and grant_label empty.
  • first_message_source is account_after_accept when the account's own message after acceptance fills empty first messages, workspace_after_accept when the defaults' step does, and none otherwise.
  • readiness.reasons says why nothing runs, such as automation_unavailable until S'more's first check of the account finishes, monitoring_off, account_campaign_missing, account_campaign_inactive, template_unset, email_request_unavailable, approval_lapsed, paused or off.
  • email.adoption is mailbox_not_connected, needs_pairing, pairing_unconfirmed, needs_mailbox_owner or adopted, the last once the mailbox owner approved the latest email revision. A member doesn't see mailbox_id.

Change an account​

The PATCH takes:

FieldRules
expected_revisionRequired. The account's automation revision, 0 before the first activation
statusactive, paused or off. Send status, exceptions or both
defaults_revisionThe defaults' current revision. Required to activate an account that's off, and with any exceptions. Resuming a paused account doesn't need it
exceptionsAn object with any of after_accept_delay_seconds, follow_up_delay_seconds, steps and templates, using the defaults' rules. Leave it out to keep the account's exceptions, or send null to clear them. Only with status active or no status
  • Activating binds the defaults at defaults_revision, with the exceptions over them, under your key. From off or lapsed it sets a new cutoff_at.
  • Pausing holds the steps S'more hasn't started, and resuming lets them run again. Neither needs the approval scopes, and neither changes the approval.
  • Turning off cancels every step S'more hasn't started, and keeps the account's exceptions for the next activation.
  • A message that's being sent, or was sent, isn't changed by any of these.

These change LinkedIn steps only. Email has its own setting in Email, and pausing or turning off LinkedIn automation isn't checked before an email is scheduled.

The answer is the account's automation as GET reads it. A stale expected_revision or defaults_revision answers 409 revision_conflict. A change the account can't take answers 409 automation_not_ready with reasons, such as pausing an account that's off:

{ "code": "automation_not_ready", "message": "LinkedIn automation is not ready for this account. Reasons: off.", "runsRemaining": 0, "reasons": ["off"] }

Activating with defaults that run request_email while email requests are closed answers 409 automation_not_ready with reasons ["email_request_unavailable"]. Exceptions that leave request_email out can still be activated. Exceptions whose steps include request_email, including ones kept from before, answer 400 invalid_request with field exceptions.steps.

Monitoring​

Method and queryScopeAuthorityDoes
GET /linkedin/settings?account_id=&section=monitoringlinkedin:activity:readAny memberThe account's monitoring hours, limits and how current S'more's view of it is
PATCH /linkedin/settings?account_id=&section=monitoringlinkedin:automation:write, linkedin:inbox:readOwner or admin. No test covers the refusal without linkedin:inbox:read yetReplaces the account's monitoring settings

An account with no monitoring saved answers 404 not_found to the GET.

{
"connection_id": "55555555-5555-4555-8555-555555555555",
"sender_urn": "urn:li:fsd_profile:alex-kim",
"revision": 3,
"enabled": true,
"auto_enroll": false,
"time_zone": "America/New_York",
"windows": [{ "day": 1, "start": "09:00", "end": "17:00" }],
"daily_limit": 100,
"cycle_limit": 20,
"daily_invitation_limit": 0,
"invitation_note_limit": 200,
"tracking_state": "current",
"initial_observation_pending": false,
"next_check_at": "2026-10-30T12:00:00Z"
}

tracking_state, inbox_coverage, acceptance_coverage, next_check_at and initial_observation_pending say how current S'more's view of the account is. invitation_note_limit is the longest invitation note the workspace can approve, in UTF-16 code units.

The PATCH takes:

FieldRules
expected_revisionRequired. The monitoring revision, or 0 for the first save
enabledRequired. true turns monitoring on
time_zoneRequired. An IANA zone such as America/New_York
windowsRequired. 1 to 21 working-hours windows of day, start and end. day is 0 for Sunday to 6 for Saturday, and start and end are local times such as 09:00, with end up to 24:00. Windows on one day can't overlap, and a window can't cross midnight, so split overnight hours in two
daily_limitRequired. 1 to 300 LinkedIn requests a UTC day for this account, reads and writes together. It isn't an invitation limit
cycle_limitRequired. 1 to 20 requests in one check, and no more than daily_limit
daily_invitation_limitOptional. 0 to 300 invitations a day in time_zone. Leave it out to keep the saved value, or send 0 to remove the limit
auto_enrollOptional. Leave it out to keep the saved value
interval_minutesOptional. 30 to 1,440. S'more accepts it, but it doesn't change how often S'more checks the account

S'more checks a monitored account once every 45 to 90 minutes within its working hours, and automated messages go only within them. Turning monitoring on schedules those checks on LinkedIn and sets initial_observation_pending until the first one finishes. Until then the account's automation reads automation_unavailable.

A save also sets up the account's campaign if it has none, and the message after acceptance and automation need it. If the answer is lost, read the monitoring again before you resend.

The answer is the monitoring settings as GET reads them. A stale expected_revision answers 409 activity_conflict, so read the monitoring again and send its revision.

After accept​

note

The message after acceptance isn't enabled in production yet. Until it is, every section=after_accept mode answers 404 not_found.

The message after acceptance is one exact text an owner or admin approves for one account. S'more plans it no more than once for each person who accepts after it was turned on and has no first message of their own.

Method and queryScopeAuthorityDoes
GET /linkedin/settings?account_id=&section=after_acceptlinkedin:inbox:readOwner or adminThe account's message after acceptance
GET ...&section=after_accept&view=suggestionslinkedin:inbox:readOwner or adminTexts the account's person sent by hand on LinkedIn, newest first
PATCH /linkedin/settings?account_id=&section=after_acceptlinkedin:automation:write, linkedin:inbox:read, linkedin:outreach:send. action=approve also needs admin:writeOwner or adminApproves exact text, or turns the message off

An account with no campaign yet, such as one whose monitoring was never saved, answers 404 not_found, and a member gets 403 admin_required.

{
"account_campaign_id": "77777777-7777-4777-8777-777777777777",
"sender_urn": "urn:li:fsd_profile:alex-kim",
"status": "on",
"text": "Thanks for connecting, glad to be in touch.",
"revision": 1,
"approved_at": "2026-10-30T12:00:00Z",
"approved_by": "33333333-3333-4333-8333-333333333333",
"approved_by_name": "Alex Kim",
"lapsed_at": null,
"sent_to": 0,
"shared_email": 0,
"accepted_before": 12,
"waiting": 0
}

status is off, on or lapsed. sent_to counts the people it reached and shared_email those of them who later shared an email address. accepted_before counts people who accepted before it was turned on, who never get it, and waiting the people it filled whose message hasn't gone yet.

The suggestions answer items, each a text the account's person sent by hand with first_sent_at, last_sent_at, sent_to and shared_email:

{
"items": [
{ "text": "Thanks for connecting! What are you working on this quarter?", "first_sent_at": "2026-10-27T12:00:00Z", "last_sent_at": "2026-10-30T12:00:00Z", "sent_to": 4, "shared_email": 1 }
]
}

The PATCH takes:

FieldRules
actionRequired. approve or off
expected_revisionRequired. The message's revision, 0 before the first approval
textRequired with approve, and not sent with off. The exact message, up to 1,000 characters
  • Approving while it's off, or after it lapsed, starts a new cut-off at that moment. Approving new text while it's on keeps the cut-off.
  • The approval binds your key, and lapses as other approvals do.
  • Turning it off takes it back from the people it filled but hasn't sent to, so they have no first message again.

The answer is the message as GET reads it. A stale expected_revision answers 409 activity_conflict.

Email​

note

LinkedIn email isn't enabled in production yet, and neither are the Gmail accounts it sends from. Until they are, every section=email mode answers 404 not_found. A key without the mode's Gmail scope gets 403 insufficient_scope first, and API keys says when the Gmail scopes can be given.

The email section sets up email for one LinkedIn account: which Gmail mailbox sends it, which revision of the email template, and whether each email waits for the mailbox owner. A mailbox is connected on Gmail accounts first. The emails S'more writes are read and sent on LinkedIn API.

Method and queryScopeAuthorityDoes
GET /linkedin/settings?account_id=&section=emailgmail:readAny member. Members don't see the template copy, the sending address or the pairingsThe account's email policy, pairings and template binding
PATCH ...&section=email&action=request_pairinggmail:writeThe mailbox's owner, who is an owner or adminAsks for their own mailbox to send this account's email
PATCH ...&section=email&action=confirm_pairinggmail:writeThe workspace ownerConfirms a mailbox that asked. It sends nothing and approves no template
PATCH ...&section=email&action=remove_pairinggmail:writeThe workspace owner, or the mailbox's owner who is an adminRemoves the mailbox from this account
PATCH ...&section=email&action=approvegmail:sendThe owner of the confirmed mailbox, who is an owner or adminApproves a template revision and a mode under the caller's key
PATCH ...&section=email&action=offgmail:writeAny owner or adminTurns the account's email off

Every mode answers the email section:

{
"connection_id": "55555555-5555-4555-8555-555555555555",
"sender_urn": "urn:li:fsd_profile:alex-kim",
"policy": {
"available": true,
"policy": {
"mailbox_id": "44444444-4444-4444-8444-444444444444",
"mode": "ask",
"subject": "Great to connect, {first_name}",
"body": "Hi {first_name},\n\nThanks for sharing your email.\n\n{signature}",
"calendar_url": "",
"signature": "Alex Kim",
"daily_cap": 20,
"approved_by_name": "Alex Kim",
"approved_at": "2026-10-30T12:00:00Z",
"armed_at": "2026-10-30T12:00:00Z",
"revision": 1,
"from_email": "alex@example.com",
"from_name": "Alex at Example",
"mailbox_status": "connected"
},
"sent_today": 0,
"daily_cap": 20,
"hours": { "time_zone": "America/New_York", "windows": [{ "day": 1, "start": "09:00", "end": "17:00" }] },
"can_edit": true,
"confirmed_mailbox_id": "44444444-4444-4444-8444-444444444444",
"counts": { "waiting_ok": 0, "held": 0, "scheduled": 0, "sending": 0, "sent": 0, "unknown": 0, "failed": 0, "cancelled": 0, "replied": 0, "bounced": 0 }
},
"pairing": { "confirmed": { "mailbox_id": "44444444-4444-4444-8444-444444444444", "revision": 3 }, "requested": [] },
"binding": { "template_revision": 1, "current_template_revision": 2, "in_force": true, "approved_via": "api", "lapsed": false }
}
  • policy.policy is null until email is first approved, and its revision is what approve and off send back. mode is ask, auto or off.
  • hours are the account's monitoring hours, and email goes only within them.
  • pairing has the confirmed mailbox and every mailbox waiting for the workspace owner, each with the mailbox's own revision. A member gets pairing null and the template fields empty.
  • binding.in_force is true while an approval made over the API is the one that sends. approved_via is api then, browser when email was approved in the S'more app, and null when it's off. lapsed is true once the approving key or role no longer allows sending.

A key without gmail:read can't read the section, and gets 403 insufficient_scope. Removing a pairing or turning email off with such a key answers the section as a member sees it, with no pairings and no template copy. The other writes give the same answer in code, but no test covers them yet, so hold gmail:read on the key you pair and approve with as well.

Pair a mailbox​

request_pairing takes mailbox_id, the caller's own mailbox, and expected_revision, that mailbox's revision from GET /gmail/accounts. When the workspace owner asks for their own mailbox, the pairing is confirmed at once. Otherwise the workspace owner sends confirm_pairing with the same mailbox_id and the revision shown for it in pairing.requested. An account has at most one confirmed mailbox, and another confirmation for it answers 409 pairing_taken.

remove_pairing takes mailbox_id only. It turns the account's email off and cancels the emails waiting to send from that mailbox. Removing it again is safe.

Approve email​

action=approve takes:

FieldRules
mailbox_idRequired. The confirmed mailbox, which must be the caller's own
template_revisionRequired. A saved revision of the email template
modeRequired. ask or auto
expected_revisionRequired. 0 while policy.policy is null, otherwise policy.policy.revision
daily_capOptional. 1 to 50 emails a day from this mailbox. A new policy starts at 20, and an approval without it keeps the current cap

The approval sends nothing itself. From armed_at on, S'more reads the LinkedIn replies this account receives, and when a person types an email address it writes the approved template for them to that address:

  • In ask mode every email is held until the mailbox owner sends it.
  • In auto mode an email is scheduled to send on its own within the account's hours and the daily cap. It's held for the mailbox owner instead when the reply has more than one address, a shared inbox address or words asking not to be contacted, when it arrived too late, or when someone wrote to the person on LinkedIn after that reply. An opt-out or pause recorded in LinkedIn automation isn't checked before an email is scheduled, so a person who opted out or was paused there can still be emailed in auto mode. In ask mode the held email doesn't show the opt-out either.

A reply that arrived before armed_at, or while email was off, is never emailed. The answer adds rebound, the number of waiting emails that took the newly approved text, and kept, the number of the account's other waiting emails that keep their text, such as ones a person edited or released. An approval that would rewrite more than 1,000 waiting emails answers 409 waiting_emails_limit and changes nothing.

Only the mailbox owner approves. Another admin gets 403 mailbox_owner_required, and a key without gmail:send gets 403 insufficient_scope. A template_revision that wasn't saved answers 400 invalid_request with field template_revision, and an account with no confirmed mailbox 409 pairing_required.

When the approving key is revoked, expires or loses gmail:send, or the mailbox owner is no longer an owner or admin, the approval lapses. S'more then turns the account's email off and cancels the emails waiting to send, and replies that arrive before a new approval are never emailed. Approving or turning off email in the S'more app also ends an approval made over the API.

Turn email off​

action=off takes expected_revision, the policy's revision. Any owner or admin can send it, and sending it again is safe. A reply that arrives while email is off is never emailed, even after a new approval.

Errors​

StatusCodeMeaning
400invalid_requestA field doesn't fit, and field names it when S'more can, such as merge_field, templates.follow_up, defaults_revision, exceptions or template_revision. Also an account_id that names no LinkedIn account in this workspace
400invalid_emailThe email template can't be saved as written. field names the part
403admin_requiredOnly owners and admins can do this, or your role changed since the key was made
403owner_requiredOnly the workspace owner confirms a pairing
403mailbox_owner_requiredOnly the mailbox's owner can do this
403insufficient_scopeThe key lacks a scope this mode needs, such as admin:write or gmail:send
404not_foundNo such revision, no monitoring or campaign for the account yet, or a part of LinkedIn this workspace can't use yet. See Gates
404mailbox_not_foundNo connected mailbox with that mailbox_id in this workspace, on a pairing change
409revision_conflictA template, the defaults, an account's automation or the email policy changed since you read it
409activity_conflictMonitoring or the message after acceptance changed since you read it
409automation_not_readyThe account can't take this change yet. reasons says why
409pairing_required, pairing_taken, pairing_missingEmail needs a confirmed mailbox first, the account already has one, or there's no request to confirm
409mailbox_not_connectedThe mailbox needs attention before it can send. Check its status on GET /gmail/accounts
409waiting_emails_limitThe approval would rewrite more than 1,000 waiting emails

Reads are safe to repeat. A write that timed out or lost its answer may still have gone through, so read the section again before you resend, and send the change with the revision you read. A resend with the old revision answers 409 once the first write went through, except apply and remove_pairing, which are safe to send again as they were. API conventions covers the rest.