Skip to content

Commit 6a02d62

Browse files
authored
fixed links, formatting (#148)
<!-- If this pull request closes an issue, please mention the issue number below --> Closes # <!-- Issue # here --> ## 💸 TL;DR <!-- What's the three sentence summary of purpose of the PR --> ## 📜 Details [Design Doc](<!-- insert Google Doc link here if applicable -->) [Jira](<!-- insert Jira link if applicable -->) <!-- Add additional details required for the PR: breaking changes, screenshots, external dependency changes --> ## 🧪 Testing Steps / Validation <!-- add details on how this PR has been tested, include reproductions and screenshots where applicable --> ## ✅ Checks <!-- Make sure your pr passes the CI checks and do check the following fields as needed - --> - [ ] CI tests (if present) are passing - [ ] Adheres to code style for repo - [ ] Contributor License Agreement (CLA) completed if not a Reddit employee
1 parent 7842b5a commit 6a02d62

4 files changed

Lines changed: 159 additions & 191 deletions

File tree

Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
# External Endpoints
22

3-
External endpoints provide a secure way for external services to communicate with your Devvit app.
3+
External endpoints provide a secure way for external services to communicate with your Devvit app.
44

55
:::note
6-
This is a limited-access feature. Fill out this \[form\](https://docs.google.com/forms/d/e/1FAIpQLScLU2m-IH9xtt4hqFBNy5AlrswY0pvfvoyTiQREbq\_9xDQJkQ/viewform) to request access.
6+
This is a limited-access feature. Fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLScLU2m-IH9xtt4hqFBNy5AlrswY0pvfvoyTiQREbq_9xDQJkQ/viewform) to request access.
77
:::
88

99
The term **endpoint** refers to externally accessible routes exposed by your app. This consistent terminology makes it easier to identify the relevant APIs, configuration, and code paths.
1010

11-
Devvit supports two types of tokens:
11+
Devvit supports two types of tokens:
1212

13-
| Type | Best For | Authentication |
14-
| :---- | :---- | :---- |
13+
| Type | Best For | Authentication |
14+
| :-------------- | :--------------------------------------------------------------------- | :----------------------------------------------------------- |
1515
| Callback tokens | Your app initiates work and an external service returns a result later | Short-lived, one-time callback token generated automatically |
16-
| Managed tokens | An external service initiates requests to your app | Long-lived token managed in Developer Settings |
16+
| Managed tokens | An external service initiates requests to your app | Long-lived token managed in Developer Settings |
1717

1818
All tokens start with `devvit_at_`.
1919

2020
Tokens are secret. Never share a token or a callback URL.
2121

2222
Never call an `/external/` endpoint from a Devvit app’s frontend. All external endpoints require a secret token to access which cannot easily be hidden in a post. Call conventional `/api/` endpoints instead. External endpoints are for trusted services only.
2323

24-
## **Declaring external endpoints**
24+
## Declaring external endpoints
2525

2626
External endpoints are declared in `devvit.json`.
2727

@@ -41,18 +41,18 @@ The key becomes the endpoint name used throughout the codebase, while the value
4141

4242
External endpoints can receive request bodies up to 10 megabytes, and are limited to 5 requests per second (at time of writing, may change at any time).
4343

44-
## **Callbacks**
44+
## Callbacks
4545

46-
Callbacks allow your Devvit app to generate authenticated URLs that external services can use to call *back* into the app.
46+
Callbacks allow your Devvit app to generate authenticated URLs that external services can use to call _back_ into the app.
4747

4848
These URLs contain short-lived authentication tokens generated at runtime. You can think of Callbacks as similar to passwordless login links ("magic links") used by services such as Slack.
4949

5050
Typical flow:
5151

52-
1. Your app generates a callback URL.
53-
2. Your app sends that URL to an external service.
54-
3. The external service performs work.
55-
4. The external service invokes the callback URL.
52+
1. Your app generates a callback URL.
53+
2. Your app sends that URL to an external service.
54+
3. The external service performs work.
55+
4. The external service invokes the callback URL.
5656
5. Your app is invoked and receives the request.
5757

5858
### Callback URL format
@@ -65,12 +65,12 @@ https://<app_slug>-<subreddit_id>-external.devvit.net/external/<your_app_defined
6565

6666
Callback tokens are generated automatically when creating a callback URL, and you won’t interact with them directly. Callback tokens:
6767

68-
* Do not have names
69-
* Are not visible in Developer Settings
70-
* Have a short time-to-live.
71-
* Are intended for single use
72-
* Are generated at runtime
73-
* Currently scoped to an installation
68+
- Do not have names
69+
- Are not visible in Developer Settings
70+
- Have a short time-to-live.
71+
- Are intended for single use
72+
- Are generated at runtime
73+
- Currently scoped to an installation
7474

7575
### Using callbacks
7676

@@ -81,64 +81,63 @@ Callbacks are best suited for workflows where the Devvit app initiates work in a
8181
Inside your app, generate a callback URL and pass it to an external service.
8282

8383
```ts
84-
import { externalEndpoints } from '@devvit/web/server';
84+
import { externalEndpoints } from "@devvit/web/server";
8585

8686
// Get URL with a secret new token. Looks something like:
8787
// https://foobar-abc123-external.devvit.net/external/on/comment-processed?externalToken=devvit_at_def456
88-
const url = await externalEndpoints.getCallbackUrl('onCommentProcessed');
88+
const url = await externalEndpoints.getCallbackUrl("onCommentProcessed");
8989

9090
// A normal fetch but include the callback URL in the request body.
91-
const rsp = await fetch('https://your-service.example.com/process', {
92-
method: 'POST',
91+
const rsp = await fetch("https://your-service.example.com/process", {
92+
method: "POST",
9393
headers: {
94-
'Content-Type': 'application/json',
95-
'Accept': 'application/json'
94+
"Content-Type": "application/json",
95+
Accept: "application/json",
9696
},
9797
body: JSON.stringify({
9898
// You can put any data you want here but you must pass the
9999
// callback URL somewhere or the external service won't be able
100100
// to call back.
101-
callbackUrl: url
101+
callbackUrl: url,
102102
}),
103103
});
104104
if (!rsp.ok) {
105-
const text = await msg.text().catch(() => '');
105+
const text = await msg.text().catch(() => "");
106106
throw Error(`HTTP status ${rsp.status}: ${rsp.statusText}; ${text}`);
107107
}
108-
109108
```
110109

111110
When called, `getCallbackUrl()`:
112111

113-
* Generates a URL with a callback token; send the URL with whatever data your external service needs
114-
* Saves the current request context for restoration on callback including all context fields like `Context.postId`.
115-
112+
- Generates a URL with a callback token; send the URL with whatever data your external service needs
113+
- Saves the current request context for restoration on callback including all context fields like `Context.postId`.
116114

117-
118115
The generated callback URL:
119116

120-
* Expires after 10 minutes (at time of writing, may change at any point)
121-
* Can only be used once
117+
- Expires after 10 minutes (at time of writing, may change at any point)
118+
- Can only be used once
122119

123120
#### Step 2: Invoke the Callback from an External Service
124121

125122
Your external service receives the callback URL and calls it when processing is complete.
126123

127124
```ts
128-
129125
// Invoke the callback URL.
130-
const rsp = await fetch(`https://${appSlug}-${subredditId}-external.devvit.net/external/on/comment/processed?externalToken=${token}`, {
131-
method: 'POST',
132-
headers: {
133-
'Content-Type': 'application/json',
134-
'Accept': 'application/json'
126+
const rsp = await fetch(
127+
`https://${appSlug}-${subredditId}-external.devvit.net/external/on/comment/processed?externalToken=${token}`,
128+
{
129+
method: "POST",
130+
headers: {
131+
"Content-Type": "application/json",
132+
Accept: "application/json",
133+
},
134+
body: JSON.stringify({
135+
// Whatever data your app wants from your external service.
136+
}),
135137
},
136-
body: JSON.stringify({
137-
// Whatever data your app wants from your external service.
138-
}),
139-
});
138+
);
140139
if (!rsp.ok) {
141-
const text = await msg.text().catch(() => '');
140+
const text = await msg.text().catch(() => "");
142141
throw Error(`HTTP status ${rsp.status}: ${rsp.statusText}; ${text}`);
143142
}
144143
```
@@ -148,15 +147,13 @@ if (!rsp.ok) {
148147
Register a handler inside your app to receive the callback.
149148

150149
```ts
151-
app.post('/external/on/comment/processed', async (c) => {
150+
app.post("/external/on/comment/processed", async (c) => {
152151
const input = await c.req.json<YourExternalRequest>();
153152

154-
console.log(
155-
`Result: ${JSON.stringify(input)} in ${context.subredditId}`
156-
);
153+
console.log(`Result: ${JSON.stringify(input)} in ${context.subredditId}`);
157154

158155
return c.json<YourExternalResponse>({
159-
status: 'ok',
156+
status: "ok",
160157
});
161158
});
162159
```
@@ -165,23 +162,23 @@ app.post('/external/on/comment/processed', async (c) => {
165162

166163
All `Context` is recorded at `getCallbackUrl()` invocation time and restored when handling an external request.
167164

168-
## **Managed tokens**
165+
## Managed tokens
169166

170167
Managed tokens are long-lived credentials that allow an external service to invoke endpoints exposed by your Devvit app. Unlike callbacks, managed tokens do not require a request to originate from your app.
171168

172-
Managed tokens are:
169+
Managed tokens are:
173170

174-
* Created and managed in Developer Settings of your app
175-
* Have a user-defined name
176-
* Consist of:
177-
* A public token ID
178-
* A private secret
179-
* Long-lived
180-
* Currently scoped globally across all installations of an app
181-
* All requests execute as the app account
182-
* Can be revoked at any time
171+
- Created and managed in Developer Settings of your app
172+
- Have a user-defined name
173+
- Consist of:
174+
- A public token ID
175+
- A private secret
176+
- Long-lived
177+
- Currently scoped globally across all installations of an app
178+
- All requests execute as the app account
179+
- Can be revoked at any time
183180

184-
### Example
181+
### Example
185182

186183
```
187184
Name: my-token-123
@@ -192,10 +189,10 @@ devvit_at_abcdefg1234567890...
192189

193190
### Security notes
194191

195-
* The secret token is displayed only once, at creation time.
196-
* Store the secret securely. If you use the token in a Devvit app, use a [`secret setting`](https://developers.reddit.com/docs/capabilities/server/settings-and-secrets). Many apps won’t need to store the secret as it’s the calling external service that needs to send it.
197-
* Never expose tokens to anyone.
198-
* The secret token must be provided in the `Authorization` header for incoming external requests to the app.
192+
- The secret token is displayed only once, at creation time.
193+
- Store the secret securely. If you use the token in a Devvit app, use a [`secret setting`](https://developers.reddit.com/docs/capabilities/server/settings-and-secrets). Many apps won’t need to store the secret as it’s the calling external service that needs to send it.
194+
- Never expose tokens to anyone.
195+
- The secret token must be provided in the `Authorization` header for incoming external requests to the app.
199196

200197
Example:
201198

@@ -221,10 +218,10 @@ if (!rsp.ok) {
221218

222219
### Creating a managed token
223220

224-
1. Open the Developer Portal.
225-
2. Navigate to Developer Settings for your app.
226-
3. Create a new App Token.
227-
4. Copy and securely store the generated private secret.
221+
1. Open the Developer Portal.
222+
2. Navigate to Developer Settings for your app.
223+
3. Create a new App Token.
224+
4. Copy and securely store the generated private secret.
228225
5. The secret will only be shown once. If you lose it, revoke the token and create a new one.
229226

230227
### Scope
@@ -239,21 +236,21 @@ https://${appSlug}-bbb456-external.devvit.net/external/on/image/generated // sub
239236
https://${appSlug}-ccc789-external.devvit.net/external/on/image/generated // sub C
240237
```
241238

242-
This is a powerful and potentially problematic feature because it allows an app to expose endpoints that can be accessed from outside the platform. Moderators should carefully evaluate apps that have external endpoints capability and ensure they come from trusted sources.
239+
This is a powerful and potentially problematic feature because it allows an app to expose endpoints that can be accessed from outside the platform. Moderators should carefully evaluate apps that have external endpoints capability and ensure they come from trusted sources.
243240

244-
To help mods evaluate your app, document each external endpoint clearly, and only expose endpoints that are necessary for your app’s functionality.
241+
To help mods evaluate your app, document each external endpoint clearly, and only expose endpoints that are necessary for your app’s functionality.
245242

246-
## **Choosing between callbacks and managed tokens**
243+
## Choosing between callbacks and managed tokens
247244

248245
Use **callback tokens** when your app initiates a workflow and needs a secure way for an external service to return a result with the same context.
249246

250247
Use **managed tokens** when an external service needs ongoing access to invoke your app's external endpoints.
251248

252-
| Use Case | Callback Token | Managed Tokens |
253-
| ----- | :---: | :---: |
254-
| App initiates work in an external service | | |
255-
| External service initiates requests || |
256-
| Short-lived credentials || |
257-
| Long-lived credentials || |
258-
| One-time use URLs || |
259-
| Centralized external service || |
249+
| Use Case | Callback Token | Managed Tokens |
250+
| ----------------------------------------- | :------------: | :------------: |
251+
| App initiates work in an external service | | |
252+
| External service initiates requests || |
253+
| Short-lived credentials || |
254+
| Long-lived credentials || |
255+
| One-time use URLs || |
256+
| Centralized external service || |

sidebars.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -164,21 +164,8 @@ const sidebars: SidebarsConfig = {
164164
label: "Automation & Triggers",
165165
items: [
166166
"capabilities/server/scheduler",
167-
{
168-
type: "category",
169-
label: "Triggers",
170-
link: {
171-
type: "doc",
172-
id: "capabilities/server/triggers",
173-
},
174-
items: [
175-
{
176-
type: "doc",
177-
id: "capabilities/server/global-triggers",
178-
label: "Global Triggers",
179-
},
180-
],
181-
},
167+
"capabilities/server/triggers",
168+
"capabilities/server/global-triggers"
182169
],
183170
},
184171
{

0 commit comments

Comments
 (0)