Create a Simple Post (User)

Create post on LinkedIn using text, URL or article. [See the docs](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/posts-api?view=li-lms-2022-11&tabs=http#create-organic-posts) for more information

Script linkedin Verified

by hugo697 ยท 7/17/2024

The script

Submitted by hugo697 Typescript (fetch-only)
Verified 689 days ago
1
type Linkedin = {
2
	token: string
3
	apiVersion: string
4
}
5

6
type Base64 = string
7

8
async function initializeImageUpload(resource: Linkedin, owner: string) {
9
	const endpoint = 'https://api.linkedin.com/rest/images?action=initializeUpload'
10
	const response = await fetch(endpoint, {
11
		method: 'POST',
12
		headers: {
13
			Authorization: `Bearer ${resource.token}`,
14
			'X-Restli-Protocol-Version': '2.0.0',
15
			'Content-Type': 'application/json',
16
			'LinkedIn-Version': `${resource.apiVersion}`
17
		},
18
		body: JSON.stringify({
19
			initializeUploadRequest: {
20
				owner
21
			}
22
		})
23
	})
24

25
	if (!response.ok) {
26
		throw new Error(`HTTP error! status: ${response.status}`)
27
	}
28

29
	const data = await response.json()
30
	return data.value
31
}
32

33
async function uploadImage(uploadUrl: string, image: Base64) {
34
	const buffer = Buffer.from(image, 'base64')
35

36
	const response = await fetch(uploadUrl, {
37
		method: 'PUT',
38
		headers: {
39
			'Content-Type': 'application/octet-stream'
40
		},
41
		body: buffer
42
	})
43

44
	if (!response.ok) {
45
		throw new Error(`HTTP error! status: ${response.status}`)
46
	}
47
}
48

49
async function uploadThumbnail(resource: Linkedin, owner: string, thumbnail: Base64) {
50
	const uploadData = await initializeImageUpload(resource, owner)
51
	const uploadUrl = uploadData.uploadUrl
52
	const imageUrn = uploadData.image
53

54
	await uploadImage(uploadUrl, thumbnail)
55

56
	return imageUrn
57
}
58

59
export async function main(
60
	resource: Linkedin,
61
	postData: {
62
		author: string
63
		commentary: string
64
		visibility: 'CONNECTIONS' | 'PUBLIC' | 'LOGGED_IN' | 'CONTAINER'
65
		distribution: {
66
			feedDistribution: 'NONE' | 'MAIN_FEED'
67
		}
68
		content?: {
69
			article: {
70
				source: string
71
				title: string
72
				description?: string
73
				thumbnail?: string
74
				thumbnailAltText?: string
75
			}
76
		}
77
		isReshareDisabledByAuthor: boolean
78
	},
79
	thumbnail?: Base64
80
) {
81
	if (postData.content?.article && thumbnail) {
82
		const thumbnailUrn = await uploadThumbnail(resource, postData.author, thumbnail)
83
		postData.content.article.thumbnail = thumbnailUrn
84
	}
85

86
	// Can use unversioned API too, but using versioned one to introduce consistency with Create_a_Simple_Post__Organization_
87
	const endpoint = 'https://api.linkedin.com/rest/posts'
88

89
	const response = await fetch(endpoint, {
90
		method: 'POST',
91
		headers: {
92
			Authorization: `Bearer ${resource.token}`,
93
			'X-Restli-Protocol-Version': '2.0.0',
94
			'LinkedIn-Version': `${resource.apiVersion}`,
95
			'Content-Type': 'application/json'
96
		},
97
		body: JSON.stringify({ ...postData, lifecycleState: 'PUBLISHED' })
98
	})
99

100
	if (!response.ok) {
101
		throw new Error(`HTTP error! status: ${response.status}`)
102
	}
103

104
	const postId = response.headers.get('x-restli-id')
105
	const data = await response.json()
106

107
	return { ...data, postId }
108
}
109