Reference · 15 operations
Actions: LinkedIn
Last updated from API version 2026-10-06 at (live spec, refreshed hourly). openapi.json
Send a LinkedIn invitation to each contact.
/api/v1/actions/linkedin/connect- Scope
- act
- Credits
- 7 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. One LinkedIn invitation per contact, with an optional note
(at most 300 characters once filled). A contact already connected
to the identity is skipped ALREADY_CONNECTED, one with an
invitation pending INVITE_PENDING.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
{{firstName}}, {{lastName}}, {{company}}, {{title}},
{{headline}}, {{industry}} are filled from the stored contact,
as sequences fill them; a target missing a field is skipped
MISSING_TEMPLATE_VARIABLES, one over LinkedIn's limit TOO_LONG.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's rendered content frozen; a person
approves it in the app, then it runs. Right before each send the
content is rendered again from the contact: a contact changed since
the approval is skipped CONTENT_CHANGED. Every send passes the
send gate: the key is re-checked (API_KEY_REVOKED); suppressed,
excluded or unsubscribed contacts are skipped, and so is a contact
an active sequence is working on (IN_SEQUENCE). A person this
identity already sent the same kind of touch to within 30 days is
skipped (ALREADY_SENT, ALREADY_CONTACTED): Owlbound checks its
own send log on the identity's LinkedIn accounts (sequences, plays,
forms, the inbox and the API all write it, before and after each
request), succeeded API actions, and for messages and InMail the
person's synced LinkedIn conversations (outbound messages, including
ones sent on LinkedIn itself). Invitations sent outside Owlbound are
seen only through the connection state. The check runs again right
before the request, once the account is free. The account's daily limits, warm-up and
health apply as for sequences; a send that must wait is
scheduled (RATE_LIMITED, WARMING_UP, IDENTITY_BUSY, ...)
and runs later. Never sent twice: a send whose outcome is unknown
(timeout, provider 5xx) is checked against LinkedIn (sent
invitations, the chat) and otherwise fails OUTCOME_UNKNOWN for a
person to check; it is never retried.
7 credits (sequence.linkedin.connect) per invitation sent; skips
and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInConnectRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- notestring
The invitation note (template); at most 300 characters once filled. No note: a plain invitation.
max length 600 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (the rendered content of the first targets).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/connect" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Send a LinkedIn message to each contact.
/api/v1/actions/linkedin/dm- Scope
- act
- Credits
- 6 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. One LinkedIn message per contact (at most 8,000 characters
once filled). Needs a connection with the identity
(NOT_CONNECTED otherwise).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
{{firstName}}, {{lastName}}, {{company}}, {{title}},
{{headline}}, {{industry}} are filled from the stored contact,
as sequences fill them; a target missing a field is skipped
MISSING_TEMPLATE_VARIABLES, one over LinkedIn's limit TOO_LONG.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's rendered content frozen; a person
approves it in the app, then it runs. Right before each send the
content is rendered again from the contact: a contact changed since
the approval is skipped CONTENT_CHANGED. Every send passes the
send gate: the key is re-checked (API_KEY_REVOKED); suppressed,
excluded or unsubscribed contacts are skipped, and so is a contact
an active sequence is working on (IN_SEQUENCE). A person this
identity already sent the same kind of touch to within 30 days is
skipped (ALREADY_SENT, ALREADY_CONTACTED): Owlbound checks its
own send log on the identity's LinkedIn accounts (sequences, plays,
forms, the inbox and the API all write it, before and after each
request), succeeded API actions, and for messages and InMail the
person's synced LinkedIn conversations (outbound messages, including
ones sent on LinkedIn itself). Invitations sent outside Owlbound are
seen only through the connection state. The check runs again right
before the request, once the account is free. The account's daily limits, warm-up and
health apply as for sequences; a send that must wait is
scheduled (RATE_LIMITED, WARMING_UP, IDENTITY_BUSY, ...)
and runs later. Never sent twice: a send whose outcome is unknown
(timeout, provider 5xx) is checked against LinkedIn (sent
invitations, the chat) and otherwise fails OUTCOME_UNKNOWN for a
person to check; it is never retried.
6 credits (sequence.linkedin.send) per message sent.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInDmRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- messagestringrequired
The message (template); at most 8,000 characters once filled.
min length 1max length 16000 - conversationIdstring0
Reply in this LinkedIn conversation of the contact (one target; the conversation must be on the identity's account, else
422 CONVERSATION_ACCOUNT_MISMATCH). The reply answers the person's latest message there, frozen at approval (a newer message isCONTENT_CHANGED); each of their messages is answered at most once, and never when anything was sent to them after it. No message from them: skippedNO_MESSAGE_TO_ANSWER. No connection needed. - attachmentsarray of object
Files sent with the message (JPEG, PNG, GIF, WebP, PDF; about 500 KB in all), frozen at approval.
min items 1max items 3+ Show child fields− Hide child fields
- filenamestringrequiredmax length 200
- contentTypestringrequiredimage/jpegimage/pngimage/gifimage/webpapplication/pdf
- contentstringrequired0
Base64.
- requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (the rendered content of the first targets).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/dm" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"message": "<message>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Send a LinkedIn InMail to each contact.
/api/v1/actions/linkedin/inmail- Scope
- act
- Credits
- 38 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. One LinkedIn InMail per contact: subject (at most 200
characters) and message (at most 1,900) once filled. The account
needs InMail (Sales Navigator, Recruiter or Premium) and credits
(NO_INMAIL_CREDITS otherwise).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
{{firstName}}, {{lastName}}, {{company}}, {{title}},
{{headline}}, {{industry}} are filled from the stored contact,
as sequences fill them; a target missing a field is skipped
MISSING_TEMPLATE_VARIABLES, one over LinkedIn's limit TOO_LONG.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's rendered content frozen; a person
approves it in the app, then it runs. Right before each send the
content is rendered again from the contact: a contact changed since
the approval is skipped CONTENT_CHANGED. Every send passes the
send gate: the key is re-checked (API_KEY_REVOKED); suppressed,
excluded or unsubscribed contacts are skipped, and so is a contact
an active sequence is working on (IN_SEQUENCE). A person this
identity already sent the same kind of touch to within 30 days is
skipped (ALREADY_SENT, ALREADY_CONTACTED): Owlbound checks its
own send log on the identity's LinkedIn accounts (sequences, plays,
forms, the inbox and the API all write it, before and after each
request), succeeded API actions, and for messages and InMail the
person's synced LinkedIn conversations (outbound messages, including
ones sent on LinkedIn itself). Invitations sent outside Owlbound are
seen only through the connection state. The check runs again right
before the request, once the account is free. The account's daily limits, warm-up and
health apply as for sequences; a send that must wait is
scheduled (RATE_LIMITED, WARMING_UP, IDENTITY_BUSY, ...)
and runs later. Never sent twice: a send whose outcome is unknown
(timeout, provider 5xx) is checked against LinkedIn (sent
invitations, the chat) and otherwise fails OUTCOME_UNKNOWN for a
person to check; it is never retried.
38 credits (sequence.inmail.send, the catalog price) per InMail
sent.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInInmailRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- subjectstringrequired
The InMail subject (template); at most 200 characters once filled.
min length 1max length 400 - messagestringrequired
The InMail body (template); at most 1,900 characters once filled.
min length 1max length 3800 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (the rendered content of the first targets).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/inmail" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"subject": "<subject>",
"message": "<message>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
View each contact's LinkedIn profile.
/api/v1/actions/linkedin/view_profile- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. Opens each contact's LinkedIn profile from the identity's
account: with notify (default true) the person sees "viewed your
profile"; notify: false views it without the notification (still
counted against the account's daily profile views).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.view) per profile viewed; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInViewProfileRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- notifyboolean0
Default true: the person sees that the identity viewed their profile. False: no notification.
- requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/view_profile" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Follow each contact on LinkedIn.
/api/v1/actions/linkedin/follow- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. The identity follows each contact's profile (counted against
the account's 50 follows a day, shared with sequences).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per follow; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInFollowRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/follow" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Unfollow each contact on LinkedIn.
/api/v1/actions/linkedin/unfollow- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. The identity stops following each contact's profile (counted
against the account's follow limit, like a follow).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per unfollow; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInFollowRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/unfollow" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Like a post of each contact.
/api/v1/actions/linkedin/like- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. Likes input.postUrl (then the request targets exactly the
post's author, one contact) or, without it, each contact's latest post
(none: skipped NO_POSTS). Never twice per post: a post this identity
already liked within 30 days is skipped.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per like; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInLikeRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- postUrlstring
The post (URL or URN). With it the request targets exactly one contact, the author. Without it: each contact's latest post.
max length 2000 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/like" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Comment on a post of each contact.
/api/v1/actions/linkedin/comment- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. Comments input.comment (a template filled from the stored
contact: {{firstName}}, {{company}}, ...; at most 1,250 characters
once filled) on input.postUrl (then exactly one target, the post's
author) or, without it, on each contact's latest post (none: skipped
NO_POSTS). Never twice per post.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per comment; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInCommentRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- postUrlstring
The post (URL or URN). With it the request targets exactly one contact, the author. Without it: each contact's latest post.
max length 2000 - commentstringrequired
The comment (template); at most 1,250 characters once filled.
min length 1max length 2500 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/comment" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"comment": "<comment>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Repost a contact's post.
/api/v1/actions/linkedin/repost- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows
- Max targets
- 1
- Dry run
- Supported
Send. The identity reposts input.postSocialId (the post's URN or
URL), with an optional comment (template, at most 3,000 characters
once filled). One target: the post's author. Never twice per post.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per repost (10 a day per workspace); skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInRepostRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- postSocialIdstringrequired
The post to repost (its URN, e.g. urn:li:activity:123, or URL).
min length 1max length 2000 - commentstring
Optional text above the repost (template); at most 3,000 characters once filled.
max length 6000 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/repost" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"postSocialId": "<postSocialId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
actions.linkedin.reply_comment
Reply to a contact's comment on a post.
/api/v1/actions/linkedin/reply_comment- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows
- Max targets
- 1
- Dry run
- Supported
Send. A threaded reply input.text (template, at most 1,250
characters once filled) to comment commentId on post
postExternalId. One target: the comment's author. Never twice per
comment.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per reply; skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInReplyCommentRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- postExternalIdstringrequired
The post the comment is on (its id or URN).
min length 1max length 500 - commentIdstringrequired
The comment to reply to.
min length 1max length 500 - textstringrequired
The reply (template); at most 1,250 characters once filled.
min length 1max length 2500 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/reply_comment" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"postExternalId": "<postExternalId>",
"commentId": "<commentId>",
"text": "<text>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Endorse a skill of each contact.
/api/v1/actions/linkedin/endorse- Scope
- act
- Credits
- 2 credits per target
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. Endorses input.skillName on each contact's profile. LinkedIn
lets only first-degree connections endorse: a contact not connected to
the identity is skipped NOT_CONNECTED; a skill the profile does not
list fails PROVIDER_REFUSED (free).
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
2 credits (sequence.linkedin.engage) per endorsement (20 a day per workspace); skips and failures are free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInEndorseRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- skillNamestringrequired
The skill to endorse, as the profile lists it.
min length 1max length 100 - requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 402 Not enough credits (INSUFFICIENT_CREDITS). With an Idempotency-Key this
result is stored and replayed: after adding credits, retry with a new
Idempotency-Key.
· 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/endorse" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"skillName": "<skillName>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Withdraw the pending invitation to each contact.
/api/v1/actions/linkedin/withdraw- Scope
- act
- Credits
- See the catalog entry
- Idempotency-Key
- required
- Class
- send
- Approval floor
- optional
- Runs
- Async
- Targets
- contact, contacts, rows, view
- Max targets
- 1,000
- Dry run
- Supported
Send. Withdraws the identity's pending LinkedIn invitation to each
contact, found in the account's sent invitations before anything is
claimed (none: skipped NO_PENDING_INVITE); the contact's connection
status becomes withdrawn. Withdrawing only lowers contact, so a
suppressed, excluded or unsubscribed contact's invitation is still
withdrawn. Counted against the account's 20
withdrawals a day.
Sent from the identity's own LinkedIn account (the key's bound
identity, else identityId; a session defaults to the member's
own), to each target contact's stored LinkedIn profile.
Always async and reviewed by default: the action is created
pending_approval (the workspace default; the dashboard agent and
MCP always) with every target's content frozen; a person approves
it in the app, then it runs. Right before each send the content is
rebuilt from the contact: a contact changed since the approval is
skipped CONTENT_CHANGED. Every send passes the send gate: the key
is re-checked (API_KEY_REVOKED); suppressed, excluded or
unsubscribed contacts are skipped, and so is a contact an active
sequence is working on (IN_SEQUENCE). Never twice: a person this
identity already gave the same touch within 30 days is skipped
(ALREADY_SENT, ALREADY_CONTACTED), whatever sent it (sequences,
plays, the app, the API: Owlbound's send log on the identity's
LinkedIn accounts, written before and after each request, and
succeeded API actions), checked again right before the request.
The account's daily limits, warm-up and health apply as for
sequences; a send that must wait is scheduled (RATE_LIMITED,
WARMING_UP, IDENTITY_BUSY, ...) and runs later. A send whose
outcome is unknown (timeout, provider 5xx) fails OUTCOME_UNKNOWN
for a person to check; it is never retried.
Free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInFollowRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
+ Show child fields− Hide child fields
- requestApprovalboolean0
Ask a person even when the key's approval mode would not.
- identityIdstring | null0
The identity whose LinkedIn account sends. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (what each of the first targets would get).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created: pending_approval until a person approves it in the app (reviewed by default), then queued and run.Show schema
pending_approval until a person approves it in the app (reviewed by default), then queued and run.ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/withdraw" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Publish a post on the identity's LinkedIn feed.
/api/v1/actions/linkedin/post_create- Scope
- act
- Credits
- See the catalog entry
- Idempotency-Key
- required
- Class
- send
- Approval floor
- required
- Runs
- Async
- Targets
- none
- Max targets
- 1
- Dry run
- Supported
Send. One post (input.content, at most 3,000 characters; up to 2
mediaUrls, https, fetched with the SSRF guard) on the identity's
own LinkedIn feed (the key's bound identity, else identityId; a
session defaults to the member's own). Target none: the action
has one target, the post. Always reviewed: the action is created
pending_approval whatever the key's approval mode (a public post
always waits for a person); the text, media and time are frozen at
approval, and a post changed since is never published
(CONTENT_CHANGED). Published at most once: a request that may
have gone out (timeout, provider 5xx) ends OUTCOME_UNKNOWN and
is never repeated. With scheduledAt (an ISO date in the next 5
days) the post waits scheduled and goes out then, through the
same checks; approved after that time, it is skipped
SCHEDULE_PASSED (never published late). The media files are read
when the action is created and their bytes frozen too: a file
changed behind its URL is never posted (CONTENT_CHANGED); media
that cannot be read (not an image, video or PDF of at most 20 MB,
25 MB for all of a post's media) is 400. Counted against the workspace's own posts (10 a day)
and the key's daily sends; the account's health applies. Free.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInPostCreateRequest
- 0
Lists make their own targets (one per provider page).
- inputobjectrequired0
+ Show child fields− Hide child fields
- contentstringrequired
The post's text (published as written: no template).
min length 1max length 3000 - mediaUrlsarray of string
Images, a video or a PDF to attach (https URLs, fetched with the SSRF guard).
max items 2 - scheduledAtstring
Publish at this time (within the next 5 days) instead of when approved.
format: date-time
- identityIdstring | null0
The identity whose LinkedIn account posts. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The dry-run plan (the post as it would be published).Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202The action was created pending_approval; once a person approves it in the app, the post is published (or waits for scheduledAt).Show schema
pending_approval; once a person approves it in the app, the post is published (or waits for scheduledAt).ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/post_create" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "none"
},
"input": {
"content": "<content>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
actions.linkedin.check_connection
Check each contact's connection with the identity.
/api/v1/actions/linkedin/check_connection- Scope
- read
- Credits
- See the catalog entry
- Idempotency-Key
- optional
- Class
- read
- Approval floor
- none
- Runs
- Inline for one target, else async
- Targets
- contact, contacts, rows, view
- Max targets
- 100
- Dry run
- Supported
Whether each contact is a first-degree connection of the identity
(the account's relations list, cached up to 6 hours) and, when not,
whether an invitation from it is pending (the account's sent
invitations). Result {connected, status} with status connected,
pending or not_connected. No profile is opened.
Read class, free, never reviewed: nothing is sent or written and
nobody is notified. Read through the identity's own LinkedIn
account (the key's bound identity, else identityId; a session
defaults to the member's own), never another workspace's. Targets:
a contact with a LinkedIn URL, contacts or table rows. One target
runs inline (200 {data: {action, result}}); more (or
mode: "async") create an async run (202); dryRun: true
returns the plan and reads nothing.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInCheckConnectionRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobject0
- identityIdstring | null0
The identity whose LinkedIn account reads. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The finished action and its result, or the dry-run plan.Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202An async run was created (queued); poll it or read the event feed.Show schema
ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/check_connection" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Check whether each contact replied on LinkedIn.
/api/v1/actions/linkedin/check_reply- Scope
- read
- Credits
- See the catalog entry
- Idempotency-Key
- optional
- Class
- read
- Approval floor
- none
- Runs
- Inline for one target, else async
- Targets
- contact, contacts, rows, view
- Max targets
- 100
- Dry run
- Supported
Whether each contact wrote to the identity on LinkedIn since
input.since (an ISO date in the past): the synced inbox first
(inbound messages of the contact's LinkedIn conversations on the
identity's account), then the chat's last messages on LinkedIn when
the inbox has nothing yet. Result {replied, repliedAt, source}.
Read class, free, never reviewed: nothing is sent or written and
nobody is notified. Read through the identity's own LinkedIn
account (the key's bound identity, else identityId; a session
defaults to the member's own), never another workspace's. Targets:
a contact with a LinkedIn URL, contacts or table rows. One target
runs inline (200 {data: {action, result}}); more (or
mode: "async") create an async run (202); dryRun: true
returns the plan and reads nothing.
Parameters
- Idempotency-Keyheaderstring
1 to 255 printable ASCII characters, scoped to the API key (a rotated key shares its predecessor's keys) and kept 24 h. Accepted on every POST, PATCH and DELETE (ignored on GET); required from v1 keys where the operation says
x-owlbound-idempotent: required(missing:400 VALIDATION_ERROR,param: Idempotency-Key). Same key and body after completion: the stored response is replayed withIdempotent-Replayed: true. Results over 64 KB are not stored; their replay is409 IDEMPOTENCY_RESULT_UNAVAILABLEwithdetails.originalRequestId(check state with a GET). Same key while the first request runs:409 IDEMPOTENCY_IN_PROGRESSwithRetry-After. Same key, other body or operation:422 IDEMPOTENCY_KEY_REUSED. A request that ended 5xx may be retried with the same key. 4xx results are stored and replayed too: a402 INSUFFICIENT_CREDITSreplays even after a top-up, so use a new Idempotency-Key after adding credits.min length 1max length 255 - Owlbound-Dry-Runheaderboolean
true: same as body fielddryRun: true(action operations). - Owlbound-Planheaderstring
A
planIdfrom a dry run: same as the body fieldplanId(action operations). A different bodyplanIdis400.pattern ^plan_[0-9a-f]{48}$
Request body (required) · application/json
LinkedInCheckReplyRequest
- targetone ofrequired0
+ Show child fields− Hide child fields
- inputobjectrequired0
+ Show child fields− Hide child fields
- sincestringrequired
Look for replies after this time (ISO 8601, in the past).
format: date-time
- identityIdstring | null0
The identity whose LinkedIn account reads. A bound key uses its identity (another is
403 IDENTITY_NOT_ALLOWED); an unbound key must name one (422 IDENTITY_REQUIRED). - dryRunboolean0
Same as the
Owlbound-Dry-Runheader. - planIdstring
Apply this dry run's plan (same as
Owlbound-Plan). Not withdryRun: true.pattern ^plan_[0-9a-f]{48}$ - modestring
auto(default) runs inline when the operation is sync, one target would run and the class is not send;syncforces inline (422 ACTION_NOT_SYNCwhen it cannot);asyncforces a run.autosyncasync - notifyActionNotify0
Events for this action.
targetEvents: truealso writes oneaction.target.completed/action.target.failedevent per target (feed and webhooks); without it only action-level events are written, to keep the volume down.webhookis accepted and has no effect (webhook endpoints subscribe by event type).
Responses
200The finished action and its result, or the dry-run plan.Show schema
ActionRunResponse
- dataone ofrequired0
+ Show child fields− Hide child fields
One of
with action
- 0
One action run. Read it again with
GET /api/v1/actions/{id}. - resultanyrequired0
The target's result, or null when it was skipped or failed.
with plan
- 0
What a real call would do, from every check up to the first side effect. Nothing is reserved, sent, written or charged. The plan is stored for 15 minutes: send its
planIdwith the real call to run exactly what was reviewed (409 PLAN_STALEwith a new plan if anything bound changed or it expired).
- 0
Metadata on every success response.
202An async run was created (queued); poll it or read the event feed.Show schema
ActionResponse
- dataobjectrequired0
+ Show child fields− Hide child fields
- 0
One action run. Read it again with
GET /api/v1/actions/{id}.
- 0
Metadata on every success response.
Errors
400 The request is invalid (VALIDATION_ERROR, CURSOR_MISMATCH, UNSUPPORTED_FILTER) · 401 Missing, invalid, expired or revoked key · 403 Missing scope, suspended workspace or unavailable identity · 404 Unknown resource or route. A resource in another workspace is also 404 · 409 IDEMPOTENCY_IN_PROGRESS (the first request with this key is still
running; `Retry-After` is the lease left, at most 60 s) or
IDEMPOTENCY_RESULT_UNAVAILABLE (the original result was too large to
store; `details.originalRequestId`).
· 422 IDEMPOTENCY_KEY_REUSED (the key was used with another body or operation), or an action that cannot run · 429 Rate limit reached · 500 Unexpected error · 503 The backend is unreachable; retry after `Retry-After`. Every error is {error: {type, code, message, requestId, docUrl}}; see error codes.
curl -X POST "https://app.owlbound.ai/api/v1/actions/linkedin/check_reply" \
-H "Authorization: Bearer $OWLBOUND_API_KEY" \
-H "Owlbound-Version: 2026-10-06" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"target": {
"kind": "contact",
"contactId": "<contactId>"
},
"input": {
"since": "2026-10-24T09:00:00Z"
},
"dryRun": true
}'The example is a dry run: it returns a plan and runs, writes and charges nothing. See dry runs and plans.
Models on this page
Shared objects the operations above link to. Clients must ignore fields they do not know.
ActionTargetContactobject
- kind"contact"required0
- contactIdstringrequired0
ActionTargetContactsobject
- kind"contacts"required0
- contactIdsarray of stringrequired
Deduplicated, order kept.
min items 1max items 1000
ActionTargetRowsobject
Rows of one table; each row's linked contact is the subject. A row
without a linked contact is skipped NO_CONTACT (push it to
contacts first); two rows of one contact act once
(DUPLICATE_TARGET). Not available yet for some workspaces: the
call answers 409 TABLE_STORE_UNSUPPORTED.
- kind"rows"required0
- tableIdstringrequired0
- rowIdsarray of stringrequired
Deduplicated, order kept.
min items 1max items 1000
ActionTargetViewobject
A saved view of one table (viewId, its filter) and / or a
filter (the tables filter language), resolved once when the
action is created into the rows it matches then (later rows are not
added: use an action column for that). At most the operation's
maxTargets rows (422 LIMIT_EXCEEDED beyond: narrow it); no match
is 400. Then exactly as rows: each row's linked contact.
Not available yet for some workspaces: the call answers
409 TABLE_STORE_UNSUPPORTED.
- kind"view"required0
- tableIdstringrequired0
- viewIdstring0
- filterany0
A tables filter (
{column, op, value},{and: [...]},{or: [...]}), combined with the view's.
ActionNotifyobject
Events for this action. targetEvents: true also writes one
action.target.completed / action.target.failed event per target
(feed and webhooks); without it only action-level events are written,
to keep the volume down. webhook is accepted and has no effect
(webhook endpoints subscribe by event type).
- targetEventsbooleandefault false
- webhookboolean0
Actionobject
One action run. Read it again with GET /api/v1/actions/{id}.
- idstringrequired0
- kindstringrequired0
The operation id.
- classstringrequired
Safety class: decides scope, approval, caps and enforcement.
readwritespendexternal_writesend - statusstringrequiredpending_approvalqueuedrunningsucceededfailedrejectedcanceledexpiredpartial
- identityIdstring | nullrequired0
- 0
- costobjectrequired0
+ Show child fields− Hide child fields
- estimatednumberrequired0
- chargednumberrequired0
- reservationIdstring | nullrequired0
- 0
- targetobjectrequired0
+ Show child fields− Hide child fields
- kindstringrequired0
- tableIdstring0
- viewIdstring0
- columnIdstring0
The send column this action runs (tables.rows.push target column).
- outputobject | nullrequired0
- resultanyrequired0
The single target's result (sync), or null; bulk results are per target.
- 0
- 0
Who started the action.
agentis an LLM-driven caller (the in-app agent, MCP) acting for the key or member inonBehalfOf. - createdAtnumberrequired0
- updatedAtnumberrequired0
ActionApprovalobject
- modestringrequirednonealwaysover_threshold
- urlstring | nullrequired0
- decidedBystring | nullrequired0
- decidedAtnumber | nullrequired0
- expiresAtnumber | nullrequired0
ActionCountsobject
- totalintegerrequired0
- queuedintegerrequired0
- scheduledintegerrequired0
- runningintegerrequired0
- succeededintegerrequired0
- skippedintegerrequired0
- failedintegerrequired0
- canceledintegerrequired0
ActionErrorobject
- codestringrequired0
- messagestringrequired0
ActionActorobject
Who started the action. agent is an LLM-driven caller (the in-app
agent, MCP) acting for the key or member in onBehalfOf.
- kindstringrequiredapiKeysessionagentjob
- idstringrequired0
API key id, user id, agent run id or job id.
- onBehalfOfobject0
Agent only.
+ Show child fields− Hide child fields
- kindstringrequiredapiKeysession
- idstringrequired0
Planobject
What a real call would do, from every check up to the first side
effect. Nothing is reserved, sent, written or charged. The plan is
stored for 15 minutes: send its planId with the real call to run
exactly what was reviewed (409 PLAN_STALE with a new plan if
anything bound changed or it expired).
- operationIdstringrequired0
- classstringrequired
Safety class: decides scope, approval, caps and enforcement.
readwritespendexternal_writesend - identityIdstring | nullrequired0
- targetsobjectrequired0
+ Show child fields− Hide child fields
- totalintegerrequired0
- wouldRunintegerrequired0
- wouldSkiparray of objectrequired0
+ Show child fields− Hide child fields
- reasonstringrequired0
- countintegerrequired0
- samplearray of stringrequired0
- creditsobjectrequired0
+ Show child fields− Hide child fields
- estimatedMinnumberrequired0
- estimatedMaxnumberrequired0
- availablenumberrequired0
- capRemainingnumber | nullrequired0
- quotaobject | nullrequired0
LinkedIn sends: the daily budget the action spends (an estimate: warm-up, health and the identity's other sends move it; the send gate decides at send time).
bucketis the daily limit's key,identityRemainingTodaywhat the identity's account has left today,keyRemainingTodaythe key's, user's or agent's daily sends left (null: uncapped),wouldDeferthe targets that would wait for a later day,firstSlotAt/lastSlotAtwhen the first and last of them would go (ms; null past 7 days). Null for other operations.+ Show child fields− Hide child fields
- bucketstringrequired0
- identityRemainingTodaynumberrequired0
- keyRemainingTodaynumber | nullrequired0
- wouldDefernumberrequired0
- firstSlotAtnumber | nullrequired0
- lastSlotAtnumber | nullrequired0
- approvalobjectrequired0
+ Show child fields− Hide child fields
- requiredbooleanrequired0
- reasonstring | nullrequiredkey_modeoperation_floorover_thresholdagentrequestedidentity_owner
- writesobjectrequired0
- previewarray of anyrequired0
The first targets that would run (at most 5).
- planIdstringrequired
Valid until
expiresAt, for the principal that made the dry run only.pattern ^plan_[0-9a-f]{48}$ - expiresAtnumberrequired0
When the plan expires (epoch milliseconds).
ResponseMetaobject
Metadata on every success response.
- requestIdstringrequired0
- 0
- pagePageMeta0
Cursor pagination.
hasMoreis the only end signal; pages may be short. - dryRunboolean0
True only on a dry-run response (action operations).
CreditsMetaobject
- usednumberrequired0
Credits charged by this request. Reads are free.
- balancenumber0
- estimatednumber0
Dry run only (action operations).
- availablenumber0
Dry run only (action operations).
- reservationIdstring | null0
Async operations only; the job settles this reservation.
PageMetaobject
Cursor pagination. hasMore is the only end signal; pages may be short.
- nextCursorstring | nullrequired0
- hasMorebooleanrequired0
- limitintegerrequiredmin 1max 200
ActionTargetNoneobject
Lists make their own targets (one per provider page).
- kind"none"required0