Skip to main content
A send can lose its answer. The request times out, the connection drops, a proxy returns an error page, and by then the message, the invitation or the comment may or may not be on LinkedIn. Only the platform can look and tell, so the send endpoints are built to make guessing unnecessary: you give every send a key, every answer says what came of it, and a repeat under the same key is answered instead of sent. This page covers every LinkedIn endpoint that puts something in front of another person:
  • the message sends under /api/linkedin-messages/: send, send-voice, send-inmail, send-sales-nav, send-recruiter and start-group
  • the connection request, POST /api/linkedin-connection-requests/send
  • the posting verbs under /api/linkedin-posting/: comment (a comment or a reply), create-post and repost
A reaction is the one write left outside the rule, for the reason given below.

The key

client_reference is your own id for one send. It takes up to 255 characters and is compared byte for byte. Mint one per send (your task id, your outbox row id) and use the same key every time you send that one again. A key and its place make one send. The place depends on what is sent:
  • A repeat to the same place is the same send, whatever its text says. If the first send went out, the repeat answers 200 with what it made (the message or the connection request row, the comment, the post or the repost) and result.idempotent_replay: true, plus result.content_differs: true when the repeat said something else (a re-rendered template, an edited draft). Nothing is sent.
  • The same key at another place is another send. That is how a bulk run can tag all of its sends with one value.
  • The time of a scheduled post is not part of its place under a key: a retry that computes a new scheduled_at is the same post.
  • A send without a key gets a weaker net. One that matches an earlier send’s words and place (for a post, its pictures or video too) is held while that send is on its way or in doubt, and answered with it for an hour after it went out. To send the same words twice on purpose, use two keys.

Four outcomes

What a send came to is one of four values. Every error of a send endpoint carries it in error.context.send_outcome, the request’s own 422 included. An answer with no send_outcome at all, such as a gateway page or a dropped connection, is unknown to you.

What a repeat is answered

One more 409 comes from the place. When another send to the same place is running or in doubt (another message to the conversation, another invitation to the person, another comment on the post), yours waits: 409 concurrent_send_in_flight with send_outcome: not_sent, the other attempt in blocking_activity_log_sid, and retry_after. Nothing of yours went out, so send the same request again at retry_after. activity_log_sid always names an attempt of your own send. Another send’s attempt is only ever blocking_activity_log_sid. Two answers belong to one kind of send each:
  • A connection request meets LinkedIn’s own rule of one pending invitation per person. When LinkedIn refuses an invitation because one of the account’s is out to the person, the platform answers with that one: as the repeat of the same invitation, with a 409 naming it while it is in doubt, or with 422 resend_not_available and context.cause: "pending" when another invitation of the account is pending. The person never gets two.
  • A post LinkedIn answered without naming one is 409 post_not_created with send_outcome: unknown. The post may be out, so the platform reads LinkedIn for it. Repeat the same request after retry_after, never as a new post.

Asking without sending

Three endpoints answer the same question and send nothing. Ask by the key, by the key and a place, or by the activity_log_sid an answer named.
POST /linkedin/v4/api/linkedin-messages/check-sent
A comment is asked about with its verb and, when the key went to more than one post, the post:
POST /linkedin/v4/api/linkedin-posting/check-sent
item is what the send made once it is known: the message row, the connection request row, a comment’s comment_urn, or a post’s or a repost’s urns and url. result.reason says why. For not_sent it is no_send_under_key, refused, or the read that proved it (not_in_thread, not_in_invitations, not_on_linkedin). While the send is in doubt it is answer_lost, may_still_land, unprovable, or a read that could not be done (thread_unreadable for a message, unreadable for the others). The tools list a few more. When the last read is old enough, check-sent reads LinkedIn during the call, so it can take as long as an inbox read. post_place is where a post landed: feed:member for your own feed, feed:org:<id> for a company page, group:<id> for a group (group:<id>:org:<id> when a page posted into it). A key that went to more than one place, such as a bulk run’s tag, answers 422 place_required when you ask by the key alone. error.context.places lists the places. Ask again with one of them. A place the platform cannot tell from the key’s own (named in another form than the sends recorded, or a post LinkedIn could not be read for) answers 422 place_unmatched, with the places too.

What LinkedIn shows, and what it cannot

The platform settles a send in doubt by reading what LinkedIn shows, by the account and the words: A post into a group whose answer was lost stays unknown until a person looks and gives their word, and so does a page post the page’s feed has not shown.

A person’s word

The platform reads LinkedIn by itself for up to a day, and check-sent reads it on demand after that. If a send stays unknown longer than your process can wait, a person opens LinkedIn and looks. Their answer rides on the same send request and names the attempt they checked by its activity_log_sid.
  • confirmed_not_sent: it is not there. It is accepted once the attempt can no longer land. Before that the request answers 409 send_outcome_unknown with waiting_for: may_still_land and that moment in retry_after and send_decisive_at. Once accepted, the attempt is settled not_sent and your request goes out.
  • confirmed_sent: it is there. The attempt is settled sent at once and nothing is sent. The answer is a 409 with send_outcome: sent, or a 200 once what it made is stored.
A request takes one of the two, and only for an attempt of the same send. Another send’s sid answers 422 not_this_message, and confirmed_sent naming an attempt already proven not sent answers 409 confirmed_sent_contradicts.

The rule for a caller

  1. Mint one key per send and keep it with the send.
  2. On sent or a 200, record it.
  3. On not_sent, follow the error. Send the same request again later, or give up.
  4. On anything else, no answer included, keep the send pending. Ask check-sent or send the same request after retry_after. It goes out again only once the answer is not_sent, and always under the same key.
  5. Past your own deadline, a person looks and gives their word.

Example

A reply to a lead goes out under task-48213:
POST /linkedin/v4/api/linkedin-messages/send
Your client gives up after 90 seconds with no answer. A minute later you send the same request again, same key and all, and the platform answers for the first send:
A few minutes later the platform has read the conversation and found the message. It stores the row under task-48213 and sends the linkedin-messages.sent webhook, and the same request now answers 200 with that row and result.idempotent_replay: true. The lead got one message. Had the conversation shown nothing after the send could no longer land, in two reads five minutes apart, the answer would have been not_sent and the same request would have sent the message once.

In a mass action

Each send a bulk run makes goes out under a key of its own: a message, a connection request, a comment. A target’s own client_reference is used as given when the plan sends with that tool once. A client_reference T in a send step’s args is a tag: each send goes out under T:{item sid}:{step id}, so an exact search for T finds nothing. Find the run’s sends by each item’s created_object_sid or by their exact keys, and match the T: prefix in your own code when you read webhooks. Do not create the run again because T matched nothing. That sends everything twice. A send step whose answer was lost waits with wait_reason: send_outcome_unknown and asks check-sent under its key before anything goes again. The retry is in Run a mass action.

Reactions

A reaction takes no key and has no check-sent. LinkedIn keeps one reaction per account and post, so sending the same reaction again changes nothing on LinkedIn. Every error of react still names send_outcome, and after unknown you can send the same reaction again.