0

Upload Document

by
Published Oct 17, 2025

This endpoint is responsible for uploading documents for the company employees. The Documents endpoint has a rate limit of 60 requests per minute.

Script personio Verified

The script

Submitted by hugo697 Bun
Verified 235 days ago
1
//native
2
type Personio = {
3
	clientId: string
4
	clientSecret: string
5
}
6
type Base64 = string
7
/**
8
 * Upload Document
9
 * This endpoint is responsible for uploading documents for the company employees.
10
The Documents endpoint has a rate limit of 60 requests per minute.
11

12
 */
13
export async function main(
14
	auth: Personio,
15
	body: {
16
		title: string
17
		comment?: string
18
		employee_id: number
19
		category_id: number
20
		date?: string
21
		file: {
22
			base64: Base64
23
			type:
24
				| 'image/png'
25
				| 'image/jpeg'
26
				| 'image/gif'
27
				| 'application/pdf'
28
				| 'appication/json'
29
				| 'text/csv'
30
				| 'text/plain'
31
				| 'audio/mpeg'
32
				| 'audio/wav'
33
				| 'video/mp4'
34
			name: string
35
		}
36
	},
37
	X_Personio_Partner_ID?: string,
38
	X_Personio_App_ID?: string
39
) {
40
	const url = new URL(`https://api.personio.de/v1/company/documents`)
41

42
	const formData = new FormData()
43
	for (const [k, v] of Object.entries(body)) {
44
		if (v !== undefined && v !== '') {
45
			if (['file'].includes(k)) {
46
				const { base64, type, name } = v as {
47
					base64: Base64
48
					type: string
49
					name: string
50
				}
51
				formData.append(
52
					k,
53
					new Blob([Uint8Array.from(atob(base64), (m) => m.codePointAt(0)!)], {
54
						type
55
					}),
56
					name
57
				)
58
			} else {
59
				formData.append(k, String(v))
60
			}
61
		}
62
	}
63
	const response = await fetch(url, {
64
		method: 'POST',
65
		headers: {
66
			...(X_Personio_Partner_ID ? { 'X-Personio-Partner-ID': X_Personio_Partner_ID } : {}),
67
			...(X_Personio_App_ID ? { 'X-Personio-App-ID': X_Personio_App_ID } : {}),
68
			Authorization: 'Bearer ' + (await getOAuthToken(auth, 'https://api.personio.de/oauth2/token'))
69
		},
70
		body: formData
71
	})
72
	if (!response.ok) {
73
		const text = await response.text()
74
		throw new Error(`${response.status} ${text}`)
75
	}
76
	return await response.json()
77
}
78

79
async function getOAuthToken(auth: Personio, tokenUrl: string): Promise<string> {
80
	const params = new URLSearchParams({
81
		grant_type: 'client_credentials',
82
		client_id: auth.clientId,
83
		client_secret: auth.clientSecret
84
	})
85

86
	const response = await fetch(tokenUrl, {
87
		method: 'POST',
88
		headers: {
89
			Authorization: 'Basic ' + btoa(`${auth.clientId}:${auth.clientSecret}`),
90
			'Content-Type': 'application/x-www-form-urlencoded'
91
		},
92
		body: params.toString()
93
	})
94

95
	if (!response.ok) {
96
		const text = await response.text()
97
		throw new Error(`OAuth token request failed: ${response.status} ${text}`)
98
	}
99

100
	const data = await response.json()
101
	return data.access_token
102
}
103