You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 7058a18
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: README.md
+20-79Lines changed: 20 additions & 79 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,31 +1,8 @@
1
1
# External custom properties example server
2
2
3
-
This guide walks you through setting up a GitHub App that writes **external custom properties** to repositories in your organization using the external custom properties API.
3
+
A runnable GitHub App that writes external custom properties to repositories in an organization.
4
4
5
-
External custom properties allow third-party integrations (GitHub Apps) to attach metadata to repositories — such as deployment environment, owning team, service tier, compliance status, or any custom key-value data your tooling produces. These properties are scoped to a **namespace** owned by the app that created them and are visible to anyone who can view custom properties in the organization, alongside native custom properties.
6
-
7
-
---
8
-
9
-
## How it works: namespaces and registration
10
-
11
-
Before an app can write external custom properties, its installation must **register a namespace** in the organization. A namespace is identified by a **display name** you choose (for example, `acme`). Once registered:
12
-
13
-
- Every property the app writes is surfaced in GitHub as `<display_name>.<property_name>` — e.g. `acme.environment`.
14
-
- The namespace is **scoped to the app installation** within that org. Properties written by App A are separate from those written by App B.
15
-
- Registration is a **one-time, immutable** step per installation.
16
-
- Uninstalling the GitHub App unregisters it and removes the external custom properties it created.
17
-
18
-
The typical calling pattern is:
19
-
20
-
1.**Register** the namespace (`POST /orgs/{org}/properties/installations`).
21
-
2.**Batch write** values for one or more repositories (`PATCH /orgs/{org}/properties/installations/values`).
22
-
3. Optionally **create, update, or unset one named property across selected repositories** (`PATCH /orgs/{org}/properties/installations/values/{property_name}`).
23
-
4. Optionally **read registrations or property definitions** to verify setup.
24
-
5. Read values through the existing REST or GraphQL custom property APIs. External custom property names are namespace-qualified in these responses, for example `acme.environment`.
25
-
26
-
Writing values **before** registering returns an error. Who performs step 1 depends on the app's permission level — see [Permissions and who registers](#permissions-and-who-registers).
27
-
28
-
For the display name, property name, and value constraints, see the [REST API reference](#api-reference).
5
+
**New to external custom properties?** Start with [Integrating custom properties with an external system](https://docs.github.com/en/organizations/managing-organization-settings/sync-external-custom-properties), which explains what they are, how namespacing and registration work, and the permissions an app needs. This README covers configuring and running *this* server.
29
6
30
7
---
31
8
@@ -62,43 +39,11 @@ If you're new to building GitHub Apps, the [Quickstart for building GitHub Apps]
62
39
63
40
## Permissions and who registers
64
41
65
-
Access to each endpoint is controlled by the organization permission **"External custom properties for repositories"**, which has three levels: **Read**, **Write**, and **Admin**.
66
-
67
-
| Action | Endpoint | Permission required |
68
-
|--------|----------|---------------------|
69
-
| Get registered GitHub App installations |`GET /orgs/{org}/properties/installations`|**Admin**|
70
-
| Register a GitHub App installation |`POST /orgs/{org}/properties/installations`|**Admin**|
71
-
| Get external custom property definitions |`GET /orgs/{org}/properties/installations/schema`|**Read**|
72
-
| Create or update external custom property values for repositories |`PATCH /orgs/{org}/properties/installations/values`|**Write**|
73
-
| Create or update an external custom property value for repositories |`PATCH /orgs/{org}/properties/installations/values/{property_name}`|**Write**|
74
-
| Remove one property across all organization repositories |`DELETE /orgs/{org}/properties/installations/values/{property_name}`|**Write**|
75
-
76
-
There are two ways to register, depending on how much control the org wants to delegate to the app:
42
+
Before an app can write values, its installation must be registered with a display name. The app can do this itself, or an organization administrator can do it on the app's behalf — which one applies depends on the permission level the app is granted.
77
43
78
-
### Self-service apps (this guide's default)
44
+
**This example server registers itself**, so it asks for **Admin** on the "External custom properties for repositories" organization permission.
79
45
80
-
Grant the app **Admin**. When the app is installed, it **registers its own namespace** using its installation access token, then writes values. This is what the example server does.
If you prefer that an org owner keep full control over namespacing rather than delegating it to the app, grant the app only **Write**. In this model:
85
-
86
-
- The app **cannot** register itself — the registration endpoint rejects a Write-only installation.
87
-
- An **org admin**, or a user/token holding the `organization_external_properties_for_repos:admin` fine-grained permission, calls the registration endpoint **on the app's behalf**, passing the app's `installation_id`.
88
-
- Until the installation is registered, the app's value writes are rejected as not registered.
89
-
90
-
To register a Write-only app's installation as an org admin, first find the installation ID (`GET /orgs/{org}/installations`), then call:
The caller must be authorized for **Admin** on "External custom properties for repositories" in the org — this can be an **organization owner/admin**, or any user or fine-grained personal access token granted the `organization_external_properties_for_repos:admin` permission. Once registered, the Write-only app can write values normally.
46
+
For the full permission model — including when to choose Admin versus Read and write, and who can register on an app's behalf — see [Selecting permissions](https://docs.github.com/en/organizations/managing-organization-settings/sync-external-custom-properties#selecting-permissions). The access level and token types each endpoint requires are listed in [Permissions required for GitHub Apps](https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps#organization-permissions-for-external-custom-properties-for-repositories).
102
47
103
48
---
104
49
@@ -135,7 +80,7 @@ For full details on app registration, see [Registering a GitHub App](https://doc
135
80
4. Set permissions:
136
81
- Under **Organization permissions**, find **"External custom properties for repositories"** and select **Admin**.
137
82
138
-
> **Why Admin?** This example server registers its own namespace on installation, which requires Admin. If you instead want an org owner to control namespacing, select **Write**and follow [Write-only apps](#write-only-apps-org-admin-controlled-namespacing) — an org admin registers the app before it can write values.
83
+
> **Why Admin?** This example server registers its own namespace on installation, which requires Admin. If you'd rather an org owner control namespacing, select **Read and write**instead — see [Selecting permissions](https://docs.github.com/en/organizations/managing-organization-settings/sync-external-custom-properties#selecting-permissions).
139
84
140
85
5. Subscribe to events:
141
86
- Check the **Installation** event (this fires when the app is installed on an org)
@@ -174,7 +119,7 @@ For full details on app registration, see [Registering a GitHub App](https://doc
174
119
-**APP_ID**: The App ID from your app's settings page
175
120
-**WEBHOOK_SECRET**: The webhook secret you chose in Step 2
176
121
-**PRIVATE_KEY_PATH**: Path to the `.pem` file you downloaded
177
-
-**DISPLAY_NAME**: The namespace display name to register (1–15 alphanumeric characters, unique in the org). Property values appear as `<DISPLAY_NAME>.<property_name>`.
122
+
-**DISPLAY_NAME**: The namespace display name to register. Properties appear in GitHub as `<DISPLAY_NAME>.<property_name>`. For the length and character rules, see [Register an app installation for external custom properties](https://docs.github.com/en/rest/orgs/custom-properties#register-an-app-installation-for-external-custom-properties).
178
123
179
124
5. Move the downloaded `.pem` file into the `example-server/` directory (or update the path in `.env` to point to its location).
180
125
@@ -232,9 +177,9 @@ Periodic sync scheduled every 60 minutes
232
177
1. From your app's settings page, click **"Public page"** (in the left sidebar)
233
178
2. Click **Install**
234
179
3. Select your organization
235
-
4.Choose repository access:
236
-
- Since the External custom properties permission operates at the organization level, the app will be installed with access to **All repositories** by default. You won't see an option to select individual repos unless the app also has repository-level permissions.
237
-
5. Click **Install**
180
+
4.Click **Install**
181
+
182
+
> **Note:** You won't be asked to pick individual repositories. See [Install the app](https://docs.github.com/en/organizations/managing-organization-settings/sync-external-custom-properties#4-install-the-app) for why.
238
183
239
184
When the installation completes, GitHub sends an `installation.created` webhook event to your configured webhook URL. The example server registers the namespace, writes external custom properties to the org's first repository, and reads the schema back.
240
185
@@ -262,20 +207,16 @@ When the installation completes, GitHub sends an `installation.created` webhook
262
207
263
208
## API Reference
264
209
265
-
The External custom properties REST endpoints are documented in the GitHub REST API reference. Each link below covers the full request body, responses, and error cases for that endpoint.
| 2 |`POST /orgs/{org}/properties/installations`|[Register an app installation for external custom properties](https://docs.github.com/en/rest/orgs/custom-properties#register-an-app-installation-for-external-custom-properties)|
271
-
| 3 |`GET /orgs/{org}/properties/installations/schema`|[Get all external custom properties for a GitHub App installation in an organization](https://docs.github.com/en/rest/orgs/custom-properties#get-all-external-custom-properties-for-a-github-app-installation-in-an-organization)|
272
-
| 4 |`PATCH /orgs/{org}/properties/installations/values`|[Create or update external custom property values for organization repositories](https://docs.github.com/en/rest/orgs/custom-properties#create-or-update-external-custom-property-values-for-organization-repositories)|
273
-
| 5 |`PATCH /orgs/{org}/properties/installations/values/{property_name}`|[Create or update external custom property values for a property across organization repositories](https://docs.github.com/en/rest/orgs/custom-properties#create-or-update-external-custom-property-values-for-a-property-across-organization-repositories)|
274
-
| 6 |`DELETE /orgs/{org}/properties/installations/values/{property_name}`|[Remove all external custom property values for a property across all organization repositories](https://docs.github.com/en/rest/orgs/custom-properties#remove-all-external-custom-property-values-for-a-property-across-all-organization-repositories)|
275
-
276
-
Request limits, display name rules, property name and value constraints, and cleanup behavior are all documented alongside these endpoints in the [REST API reference for organization custom properties](https://docs.github.com/en/rest/orgs/custom-properties).
210
+
Each endpoint links to its entry in the [REST API reference](https://docs.github.com/en/rest/orgs/custom-properties), which documents the request body, responses, and error cases for that endpoint. Between them, those entries also cover display name rules, property name and value constraints, request size limits, and cleanup behavior. The access level and token types each endpoint requires are listed in [Permissions required for GitHub Apps](https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps#organization-permissions-for-external-custom-properties-for-repositories). The [setup guide](https://docs.github.com/en/organizations/managing-organization-settings/sync-external-custom-properties#3-create-the-automation) lists them in the order an integration typically calls them, and each has a matching helper in [`example-server/app.js`](example-server/app.js).
277
211
278
-
Each endpoint maps to a helper function in [`example-server/app.js`](example-server/app.js), so you can see a working call for every one of them.
@@ -292,7 +233,7 @@ For working examples of both, see `readRepositoryCustomPropertyValues`, `readRep
292
233
293
234
## Troubleshooting
294
235
295
-
Error responses and their meanings are documented with each endpoint in the [REST API reference](#api-reference).
236
+
Error responses and their meanings are documented with each endpoint in the [REST API reference](https://docs.github.com/en/rest/orgs/custom-properties).
0 commit comments