├── CHANGES.md ├── CHANGES_ARCHIVE.md ├── README.md ├── authentication ├── app-access-token.md ├── overview.md ├── password-flow.md ├── scope.md └── web-flows.md ├── changes ├── 0.3.0.md ├── 0.4.0.md ├── 0.4.1.md ├── 0.4.2.md ├── 0.4.3.md ├── 0.4.4.md ├── 0.4.5.md ├── 0.4.5b.md ├── 0.5.0.md ├── 0.5.1.md ├── 0.6.0.md ├── 0.7.0.md ├── 0.7.1.md ├── 0.7.2.md ├── 0.7.3.md ├── 0.7.4.md ├── 0.7.5.md ├── 0.7.6.md ├── 0.7.7.md ├── 0.8.0.md ├── 0.9.0.md ├── 0.9.1.md ├── 0.9.2.md ├── 0.9.3.md ├── 0.9.4.md ├── 0.9.5.md ├── 0.9.6.md ├── 1.0.0.md ├── 1.1.0.md ├── 1.2.0.md └── 1.3.0.md ├── how-to ├── app-streams.md ├── channel-permissions.md ├── channels-acl.md ├── channels-pm.md ├── chat-rooms.md ├── dynamic-polling.md ├── file.md ├── overview.md ├── private-messages.md └── user-streams.md ├── implementation ├── entities.md ├── libraries.md ├── overview.md ├── pagination.md ├── rate-limits.md ├── raw.md └── responses.md └── resources ├── app-streams.md ├── channels ├── explore.md ├── index.md ├── lifecycle.md ├── lookup.md ├── muting.md ├── presence.md ├── search.md └── subscribing.md ├── clients.md ├── files ├── index.md ├── lifecycle.md └── lookup.md ├── messages ├── index.md ├── lifecycle.md ├── lookup.md ├── report.md ├── search.md └── sticky.md ├── other ├── index.md ├── oembed.md ├── rss.md ├── system.md └── text-process.md ├── polls ├── index.md ├── lifecycle.md ├── lookup.md └── search.md ├── posts ├── bookmarks.md ├── explore.md ├── index.md ├── interactions.md ├── lifecycle.md ├── lookup.md ├── report.md ├── reposts.md ├── search.md ├── streams.md └── threads.md ├── stream-marker.md ├── token.md ├── user-streams.md └── users ├── badges.md ├── blocking.md ├── following.md ├── index.md ├── interactions.md ├── lookup.md ├── muting.md ├── presence.md ├── profile.md └── search.md /CHANGES.md: -------------------------------------------------------------------------------- 1 | Follow Pnut API updates via RSS 2 | 3 | # Changes 4 | 5 | 6 | ## [v1.3.0](/docs/changes/1.3.0) {#1.3.0} 7 | 8 | ### Features 9 | 10 | * Per-Channel User Presence 11 | * User Presence timeout configurable 12 | * Delete multiple files at once 13 | 14 | ### Fixes 15 | 16 | * Post search by `raw_types` failing 17 | * Data export file name referred to wrong username 18 | * V0 System Stats endpoint failing 19 | * V0 Client link entities wrong 20 | * Revising Post not checking entities 21 | 22 | *Released 2023-10-21* 23 | 24 | 25 | 26 | ## [v1.2.0](/docs/changes/1.2.0) {#1.2.0} 27 | 28 | ### Features 29 | 30 | * Suggested Users 31 | * User Presence in app streams and user streams 32 | * Files include lists of objects they are attached to 33 | * Exclude users from post search 34 | * Chat room names in E-mail digests 35 | 36 | ### Changes 37 | 38 | * Follower data included in user export 39 | 40 | ### Fixes 41 | 42 | * Stickying message without authentication returned nothing 43 | * User clients count missing on App Streams 44 | * Bitrate field for Audio file raw 45 | * Some calls not finding scopes when calling unauthenticated 46 | * V0 File Delete returns 404 47 | * V0 User Presence update response not consistent 48 | 49 | *Released 2023-04-04* 50 | 51 | 52 | 53 | ## [v1.1.0](/docs/changes/1.1.0) {#1.1.0} 54 | 55 | ### Features 56 | 57 | * "Welcoming committee" option for email digests 58 | * User followers and following can be ordered by last_post_id, followed_at, id 59 | * User search can filter by is_following, is_follower 60 | * New "256" post explore stream 61 | 62 | ### Fixes 63 | 64 | * Personal Data Request Broken on v1 65 | * Not all Chat Room HTML Converted to Entities 66 | * Changing account to bot does not properly register it as bot 67 | * Changing account to feed left some follower data intact 68 | * Developers could not create new clients 69 | * V0 Poll raw was showing in new v1 format 70 | * Channel Subscribers sometimes showing duplicates 71 | 72 | *Released 2021-07-30* 73 | 74 | 75 | 76 | ## [v1.0.0](/docs/changes/1.0.0) {#1.0.0} 77 | 78 | *Note: v0.9.6 will continue to be supported as-is until further notice.* 79 | 80 | ### Features 81 | 82 | * E-mail Digest Notifications 83 | * New Channel Fields 84 | * Chat Room Name on Messages in Streams 85 | * Chat Room Rich Text Description 86 | * Channel Explore Streams 87 | * Changing Channel Owners 88 | * Message Replies Count 89 | * NSFW Reposts 90 | * Search Improvements 91 | * Polls Added to App Streams 92 | * RSS Feeds Include Media 93 | 94 | ### Changes 95 | 96 | Breaking 97 | 98 | * User Streams and App Streams disabled for API v0 v0.9.6 99 | * file, token, and follow User Stream Events Include Data 100 | * Posts and Messages in User and App Streams 101 | * Private Message ACL Changes 102 | * Poll Response Handling Changed 103 | * Raw Objects Reformatted 104 | * User Objects No Longer Overloaded 105 | * User "users" Count Removed 106 | * Channel Owner Renamed 107 | * Message is_sticky Always Present 108 | * References to "link" Now "url" 109 | * Post Revision Now an Integer 110 | * System Stats Endpoint Adjustment 111 | * System Config Endpoint Adjustment 112 | * Text Render Endpoint Adjustment 113 | * Search Changes 114 | * User Poll Response Stream 115 | 116 | Minor 117 | 118 | * "basic" Authentication Scope Always Included v0.9.6+ 119 | * Error Responses 120 | * Animated GIF Avatars 121 | * Accepted Actions Against Reposts 122 | * E-mail Notifications for Follow, Repost, Bookmark 123 | 124 | Informational 125 | 126 | * New TLDs Recognized v0.9.6+ 127 | * Documentation Structure Reorganized 128 | 129 | 130 | ### Fixes 131 | 132 | * Channel ACL Duplicates v0.9.6+ 133 | * public_messages Scope Creep v0.9.6+ 134 | * Updating User Badge v0.9.6+ 135 | * Revised Post created_at Format v0.9.6+ 136 | * Search Fixes v0.9.6+ 137 | * International Punycode Links v0.9.6+ 138 | * Error Handling of Multiple Messages or Posts by ID 139 | 140 | *Released 2021-02-27* 141 | 142 | 143 | ## [v0.9.6](/docs/changes/0.9.6) {#0.9.6} 144 | 145 | ### Features 146 | 147 | * Used invites view 148 | * Simple QR code invite 149 | * Chat room E-mail notifications 150 | * RSS feed URI templates 151 | * MFA backup code 152 | * E-mail notification link presets 153 | 154 | ### Changes 155 | 156 | * "API changes" documentation 157 | * Markdown link length calculation documentation 158 | * Marking all of a channel type as "read" 159 | * Unread chat room count on subscribed channels 160 | * Message search "replies" flag 161 | * More flexible user messages scopes 162 | 163 | ### Fixes 164 | 165 | * Localizations 166 | * Poll search order 167 | 168 | *Released 2020-05-02* 169 | 170 | 171 | 172 | 173 | ## [Archived](/docs/changes/archived) 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PNUT.io API Features & Issues 2 | 3 | The API documentation is at https://docs.pnut.io. 4 | 5 | Request features and report API issues for Pnut in the Issues section of this repository for future consideration / inclusion. Thanks for helping us build something you want to use! 6 | 7 | 8 | ## Before Reporting 9 | 10 | If you are having operational issues (down time, slowness, something broke), or if you just want to chat, send an email to support@pnut.io. -------------------------------------------------------------------------------- /authentication/app-access-token.md: -------------------------------------------------------------------------------- 1 | # App Access Token 2 | 3 | ## Setup 4 | 5 | App access tokens can only be retrieved by developers' apps. 6 | 7 | All that is required is the `client_id` and `client_secret`. 8 | 9 | App tokens are only to be used server-side, in ways that will protect the data. They can retrieve any data that any authorized users of the app can access, so you must take care to respect users' blocks and mutes yourself. 10 | 11 | 12 | ## Process 13 | 14 | Make a POST call like so: 15 | 16 | ```bash 17 | curl "https://api.pnut.io/v1/oauth/access_token" \ 18 | -d "client_id=[client ID]" \ 19 | -d "client_secret=[client secret]" \ 20 | -d "grant_type=client_credentials" \ 21 | -X POST 22 | ``` 23 | 24 | A JSON response will be returned in the form of: 25 | 26 | ```json 27 | {"access_token":ACCESS_TOKEN, "token":{"...Token object..."}} 28 | ``` 29 | 30 | 31 | These are the endpoints available to app tokens, in addition to any endpoints that do not require authentication: 32 | 33 | * GET /users/{user_id}/following 34 | * GET /users/{user_id}/followers 35 | * GET /users/{user_id}/muted 36 | * GET /channels 37 | * GET /channels/{channel_id} 38 | * GET /channels/{channel_id}/subscribers 39 | * GET /channels/{channel_id}/messages 40 | * GET /channels/{channel_id}/messages/{message_id} 41 | * GET /channels/messages 42 | * POST /streams 43 | * GET /streams/{stream_key} 44 | * PUT /streams/{stream_key} 45 | * DELETE /streams/{stream_key} 46 | * GET /streams 47 | * DELETE /streams 48 | * GET /apps/me/users/ids 49 | * GET /apps/me/users/tokens 50 | * GET /presence 51 | -------------------------------------------------------------------------------- /authentication/overview.md: -------------------------------------------------------------------------------- 1 | # Authentication 2 | 3 | *Any user may create one client that only they can authorize (for all intents and purposes, only they can use it).* 4 | 5 | Pnut implements parts of the [OAuth 2.0 standard](https://oauth.net/2/) to secure access between clients, users, and the API. 6 | 7 | To authenticate and retrieve an app token or a token on behalf of a user, you will need to have an active client. 8 | 9 | Once you have created a client from the [developer area](https://pnut.io/dev), read through the implementation details and choose what [Scopes](../authentication/scope) you will need to authorize, and an appropriate Flow to authorize them with: 10 | 11 | * [Web Flows](../authentication/web-flows) 12 | * [Password Flow](../authentication/password-flow) 13 | 14 | If you want to retrieve data on behalf of your app, from a server, and not from an individual user's perspective, retrieve an App Access Token. This is the appropriate choice particularly for App Streams and notification servers. 15 | 16 | * [App Access Token](../authentication/app-access-token) 17 | 18 | ## Fields 19 | 20 | ### Redirect URI 21 | 22 | Redirect URIs can be any valid URL, including IP addresses, with ports, etc. It cannot contain a fragment. 23 | 24 | If a query string is included on the redirect URI you specify in the Developer management area for your client, then pnut will only authorize that specific redirect URI. If you do not specify a query parameter, any query parameters will be allowed. 25 | 26 | The special URI `urn:ietf:wg:oauth:2.0:oob` will redirect users to an HTML page where they can retrieve the access token for development purposes, or to manually enter it into your app. 27 | 28 | 29 | ### Selective Revocation & Token Groups 30 | 31 | Users can deauthorize tokens for your app by the IP address used, the token group, or all tokens at once. 32 | 33 | Token groups are optional. To specify a name to group a new token with, include `?token_group={name}` in your authorization or authentication call in the web or password flows. If implementing this feature, you may want to allow users to specify the name themselves, or create a unique group name for your device, if you can determine the device to be the same. 34 | 35 | The name is limited to 64 bytes. -------------------------------------------------------------------------------- /authentication/password-flow.md: -------------------------------------------------------------------------------- 1 | # Authentication Password Flow 2 | 3 | ## Setup 4 | 5 | Password flow must be explicitly approved on a per-client basis, by a Pnut administrator. 6 | 7 | Request access by E-mailing [support@pnut.io](mailto:support@pnut.io), telling what platform the app runs on, and the client ID. If access is granted, you will be able to get your `password_grant_secret` from the client settings in the developer area. 8 | 9 | __Do not save the user's password.__ You should take every measure to respect the user's security. 10 | 11 | Where another flow is practical, use it. For your own projects and single-instance clients, you can authorize and retrieve a token for yourself directly from the client's developer page, or you can use web flows. 12 | 13 | __You must__ display the scopes that you will be authorizing, somewhere before users login, even if on a linked section of your app. 14 | 15 | 16 | ## Process 17 | 18 | Retrieve the user's username or E-mail address, and password, directly from them. 19 | 20 | Now make a POST call with what you now have: 21 | 22 | ```bash 23 | curl "https://api.pnut.io/v1/oauth/access_token" \ 24 | -d "client_id=[client ID]" \ 25 | -d "password_grant_secret=[stand-in for the client secret]" \ 26 | -d "username=[username or E-mail address]" \ 27 | -d "password=[account password OR one-time use password]" \ 28 | -d "grant_type=password" \ 29 | -d "scope=[comma-delimited scopes]" \ 30 | -X POST 31 | ``` 32 | 33 | A JSON response will be returned in the form of: 34 | 35 | ```json 36 | {"access_token":ACCESS_TOKEN, "token":{"...Token object..."}, "user_id":USER_ID, "username":USERNAME} 37 | ``` 38 | 39 | Every time a user is authorized using the password flow, they are sent an E-mail saying what client they were authorized for and what scopes. -------------------------------------------------------------------------------- /authentication/scope.md: -------------------------------------------------------------------------------- 1 | # Scopes 2 | 3 | `scope` is a comma-separated list of which parts of the API your app can access on behalf of an authenticated user. 4 | 5 | This is what a user is shown by Pnut for each scope they are authorizing: 6 | 7 | * [**basic**](#basic) - see basic information about you 8 | * [**email**](#email) - access your email address 9 | * [**files**](#files) - manage your files 10 | * **follow** - add and remove follows, mutes, and blocks for you 11 | * [**messages**](#messages) - send and receive public and private messages 12 | * **public_messages** - send and receive public messages 13 | * [**polls**](#polls) - manage your polls 14 | * **presence** - update your global presence 15 | * **stream** - read your post streams 16 | * **update_profile** - update your name and other profile information 17 | * [**write_post**](#write_post) - create and interact with your posts 18 | 19 | 20 | 21 | 22 | 23 | ## Usage 24 | 25 | ### Extended Scopes 26 | 27 | It's encouraged that you use extended scopes whenever reasonable, as it will reduce how much your app needs to filter out, and reduce the surface area for users' data to go places they don't want it to. 28 | 29 | Channels, Files, and Polls currently allow extended scopes. Extended scopes allow you to only authorize use of the channel types that your app needs access to. For example, if you only will be dealing with private messages, you should request the `messages:io.pnut.core.pm` scope. If your app has its own public-only channel type, you could request the `public_messages:com.example.site` scope. Or if it only needs to view and delete files for itself, you could request `files:com.example.site` access. 30 | 31 | 32 | ### Choosing Scopes 33 | 34 | When authorizing scopes, users will have the choice of not authorizing all new scopes. This is an example of what they might see: 35 | 36 | Choosing scopes 37 | 38 | 39 | ### Changing Scopes 40 | 41 | If the scopes you initially requested have changed, or your users may have otherwise authorized some but not all of those scopes, you can send them back through the authorization flow and it will ask for the new scopes to be allowed. 42 | 43 | Your client can tell what scopes a user has authorized for your client by the `X-OAuth-Scopes` return header on a request, or by directly requesting their [Token](../resources/token#get-token). 44 | 45 | If your app needs to *remove* scopes for a user, you will have to delete all of the user's tokens individually. You can [request all tokens authorized for your app](../resources/users/lookup#get-apps-me-users-tokens), and [delete](../resources/token#delete-token) all of them for a user. Once all tokens are removed, the user will have to completely re-authenticate the app, and you can request different scopes. 46 | 47 | 48 | ## App Tokens 49 | 50 | In the documentation, an app scope indicates that an app token is required for the endpoint. 51 | 52 | 53 | ## Notes 54 | 55 | ### basic {#basic} 56 | 57 | If any other scope is authorized, `basic` is redundant. 58 | 59 | Because it is redundant, it will be included in the authorization options if any other scope is authorized. If your app is double-checking what scopes were authorized, be sure it does not get thrown off if it sees `basic`. 60 | 61 | Any scope will allow access to any endpoints in the API that specify any access token. That includes a user's... 62 | 63 | * following 64 | * followers 65 | * muted 66 | * blocked 67 | * presence 68 | * badges 69 | * interactions 70 | * bookmarks 71 | * public clients 72 | * ongoing operations 73 | 74 | as well as abilities to... 75 | 76 | * report a post or message 77 | * set a stream marker 78 | * delete user streams 79 | * get your current token 80 | 81 | 82 | ### email {#email} 83 | 84 | When the `email` scope is authorized, the user's tokens will include their email address. 85 | 86 | `email` cannot be authorized by a user for the first time through Password Flow. 87 | 88 | App Streams do not notify of email address changes. 89 | 90 | 91 | ### files {#files} 92 | 93 | #### Uploading Files vs Managing Files 94 | 95 | Clients that only upload files to attach to posts or messages don't need `files` authorized. If you already have `write_post` or a `messages` scope authorized, you can upload files. 96 | 97 | If a client has a `files`-related scope, it will be able to retrieve, update, attach, and delete those files without including a `file_token`. 98 | 99 | #### Special Extended File Scopes 100 | 101 | There are two special file scopes: `files:core_audio` gives access to all files with `kind: audio`, and `files:core_image` gives access to all files with `kind: image`. These do not currently trigger App Stream objects; specific file types or the whole `files` scope would need to be specified to use App Streams with files. 102 | 103 | 104 | ### messages {#messages} 105 | 106 | #### Updating presence in a channel 107 | 108 | The `presence` scope is for global user's presence. If a user access token has access to a channel, it can update the user's presence in that channel. The `presence` scope isn't relevant to channel presence permissions. 109 | 110 | 111 | ### polls {#polls} 112 | 113 | #### Uploading Polls vs Managing Polls 114 | 115 | This scope works similarly to `files`, and is not needed to create and attach polls to posts and messages, but lets users attach and delete polls without a `poll_token`. 116 | 117 | 118 | ### write_post {#write_post} 119 | 120 | `write_post` allows an app to create files and polls as well, so those can be created and attached using their returned "tokens". 121 | -------------------------------------------------------------------------------- /authentication/web-flows.md: -------------------------------------------------------------------------------- 1 | # Authentication Web Flows 2 | 3 | 4 | ## Setup 5 | 6 | Client-side flow is generally used for Javascript and other situations where the code could be inspected by a 3rd party, and they could see your `client_secret`. 7 | 8 | If you are working with mobile clients, or otherwise are embedding the web page in the app, you may want to add `&simple_login=1` to your endpoint URI, which removes the navigation and extraneous links from the authorization page. 9 | 10 | To start, set at least one redirect URI in the developer area for your client. This restricts what server or app can receive the access token at the end of the process. 11 | 12 | 13 | ## Server-side flow 14 | 15 | Start by making this GET call: 16 | 17 | ```markdown 18 | https://pnut.io/oauth/authenticate 19 | ?client_id=[client ID] 20 | &redirect_uri=[redirect URI] 21 | &scope=[comma-delimited scopes] 22 | &response_type=code 23 | ``` 24 | 25 | *To always prompt the user for authorization of scopes, even if they already have, use the `https://pnut.io/oauth/authorize` endpoint instead.* 26 | 27 | This will direct the user to a page to authorize your client for the given scopes. If they approve, it will redirect them to your redirect URI with `/?code=[CODE]` appended. 28 | 29 | If the user decides *not* to authorize your client, they will be redirected to your redirect URI with `/?error_message=resource+owner+denied+your+app+access&error=access_denied` appended. 30 | 31 | Now make a POST call with what you now have in the URL-encoded body (with a `Content-Type` of `application/x-www-form-urlencoded`): 32 | 33 | ```bash 34 | curl "https://api.pnut.io/v1/oauth/access_token" \ 35 | -d "client_id=${CLIENT_ID}" \ 36 | -d "client_secret=${CLIENT_SECRET}" \ 37 | -d "code=${CODE}" \ 38 | -d "redirect_uri=${REDIRECT_URI}" \ 39 | -d "grant_type=authorization_code" \ 40 | -X POST 41 | ``` 42 | 43 | A JSON response will be returned in the form of: 44 | 45 | ```json 46 | {"access_token":ACCESS_TOKEN, "token":{"...Token object..."}, "user_id":USER_ID, "username":USERNAME} 47 | ``` 48 | 49 | 50 | 51 | 52 | ## Client-side flow 53 | 54 | Start with this GET: 55 | 56 | ```bash 57 | https://pnut.io/oauth/authenticate 58 | ?client_id=[client ID] 59 | &redirect_uri=[redirect URI] 60 | &scope=[comma-delimited scopes] 61 | &response_type=token 62 | ``` 63 | 64 | *To always prompt the user for authorization of scopes, even if they already have, use the `https://pnut.io/oauth/authorize` endpoint instead.* 65 | 66 | This will direct the user to an authorization page just like the server-side flow. If they approve the scopes for your client, they will be redirected to your `redirect URI` with `/#access_token=[token]` appended. 67 | 68 | If the user decides *not* to authorize your client, they will be redirected to your `redirect URI` with `/#error_message=resource+owner+denied+your+app+access&error=access_denied` appended. -------------------------------------------------------------------------------- /changes/0.3.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.3.0](https://pnut.io/docs/changes/0.3.0) {#0.3.0} 2 | 3 | This is a major update to pnut.io. The following is notable. 4 | 5 | ### Features 6 | 7 | * More capable app directory 8 | * Mutes and blocks management from pnut.io 9 | * Channels 10 | * Stream markers 11 | * Client secret reset option 12 | 13 | 14 | ### Changes 15 | 16 | * `pagination_id` added to user, post, message, and channel objects in paginated responses 17 | * Following, followers, muted, and blocked user lists are now paginated 18 | * PUT/DELETE bookmark returns the post bookmarked 19 | * `presence` endpoints now include a timestamp of the last time a user was seen (even when their status is not "offline") 20 | * Bookmarks are no longer restricted to the bookmarker 21 | * `invited_by` no longer included on user objects (simply able to look it up on the invite tree on pnut.io) 22 | * Developers required to enter password on every login 23 | * Markdown links and normal links are parsed by default. To prevent parsing, must include `entities.parse_links=0` 24 | * Requests for tokens respond with errors closer to OAuth 2.0 guidelines 25 | * Removed `/posts/streams/feed`, `/posts/streams/link`, `/posts/streams/domain`, `/posts/streams/link` (more appropriate to retrieve via future search) 26 | * Moved `/system/configuration` and `/system/statistics` to `/sys/config` and `/sys/stats` 27 | 28 | 29 | ### Fixes 30 | 31 | * Inviting a user now creates a "follow" action when they auto-follow the inviter 32 | * Blocks are missing fewer edge cases 33 | * Revising a post parsing markdown links and normal links improperly 34 | * Users' list of actions executed against them could only filter by one type; now any number 35 | 36 | *Released 2016-11-24* -------------------------------------------------------------------------------- /changes/0.4.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.0](https://pnut.io/docs/changes/0.4.0) {#0.4.0} 2 | 3 | This is a major update to pnut.io. The following is notable. 4 | 5 | ### Features 6 | 7 | * `raw` data on posts, messages, users, and channels 8 | * Stream markers can be updated to the latest ID on post or message creation 9 | * Channel-specific sticky messages 10 | * Channel thread endpoint 11 | 12 | 13 | ### Changes 14 | 15 | * Revising posts saves the complete original post, where it used to only preserve the original `text` and `html` 16 | * `X-API-Version` header changed to a major.minor.bugfix format, though in 0.x.x, all are minor or bugfix changes 17 | 18 | 19 | ### Fixes 20 | 21 | * Avatars/covers not showing on pnut.io profiles 22 | * `/users/me/channels/subscribed` was ordered by ID only, now by most recent message or most recent creation, with clarifying `pagination_id` 23 | * Requesting a token did not return the list of scopes for it 24 | * New user creation did not properly handle the created "follow" action 25 | * Calls for user avatars/covers were not forwarding query parameters 26 | 27 | *Released 2017-01-07* -------------------------------------------------------------------------------- /changes/0.4.1.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.1](https://pnut.io/docs/changes/0.4.1) {#0.4.1} 2 | 3 | This is a minor update to pnut.io. 4 | 5 | ### Fixes 6 | 7 | * Usernames were case-sensitive in password_flow 8 | * `io.pnut.core.chat`-type channels were not creatable 9 | * Public channels included `you` in the ACL unnecessarily when not authenticated 10 | * Deleting channel messages sometimes did not work 11 | * `/users/me/actions` not returning follow actions under some circumstances 12 | 13 | *Released 2017-01-15* -------------------------------------------------------------------------------- /changes/0.4.2.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.2](https://pnut.io/docs/changes/0.4.2) {#0.4.2} 2 | 3 | This is a minor update to pnut.io. 4 | 5 | ### Features 6 | 7 | Submitting `entities.links` on posts, messages, and users works (it is incongruously entities.links on posts and messages, but content.entities.links on users) 8 | Markers are always included on `GET /channels/{channel_id}/messages` and can be included on `GET /channels/{channel_id}` with `include_marker=1` in the query 9 | 10 | 11 | ### Changes 12 | 13 | Deactivating accounts is more straight-forward and consistent 14 | 15 | 16 | ### Fixes 17 | 18 | Unicode `pos` and `len` on entities was not handled properly 19 | Tags inside markdown were parsed, breaking html/text 20 | `GET /users?ids=` can now be @-usernames and user IDs 21 | `is_nsfw` was not consistently handled (and sometimes ignored) 22 | 23 | *Released 2017-01-23* -------------------------------------------------------------------------------- /changes/0.4.3.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.3](https://pnut.io/docs/changes/0.4.3) {#0.4.3} 2 | 3 | This is a minor update to pnut.io. 4 | 5 | ### Features 6 | 7 | * `include_marker=1` option available on all stream marker-capable endpoints 8 | 9 | ### Changes 10 | 11 | * "-" was not handled correctly in link entities 12 | * Mutes were making messages and post actions appear multiple times 13 | 14 | *Released 2017-01-25* -------------------------------------------------------------------------------- /changes/0.4.4.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.4](https://pnut.io/docs/changes/0.4.4) {#0.4.4} 2 | 3 | This is a minor update to pnut.io. 4 | 5 | ### Features 6 | 7 | * All non-developers now have the ability to create one client that only they can authorize 8 | * Users who reposted or bookmarked posts can be included by including query parameters `include_reposted_by` and `include_bookmarked_by` 9 | * Basic view of tags at `https://pnut.io/tags/tag` and RSS for them at `https://pnut.io/feed/rss/posts/tags/tag` 10 | 11 | 12 | ### Changes 13 | 14 | * Non-markdown non-client-supplied links are restricted to known TLDs 15 | * Mentions are allowed at the end of word breaks (think: `"@`, `'@`, `(@`) 16 | * Improved pnut.io user profiles and threads 17 | 18 | 19 | ### Fixes 20 | 21 | * Suspended and deleted accounts no longer retrievable from pnut.io 22 | * `public_messages` and `messages` being authorized could result in only `public_messages` access 23 | * User RSS handles reposts properly 24 | 25 | *Released 2017-02-04* -------------------------------------------------------------------------------- /changes/0.4.5.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.5](https://pnut.io/docs/changes/0.4.5) {#0.4.5} 2 | 3 | This is a minor feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * Invites are now created automatically 8 | * Each app in the directory now has its own simple page, with expanded description 9 | * Can hide posts directed at people you do not follow 10 | * Can hide "copy mentions" on the mentions endpoint 11 | * New Explore streams for posts that are Conversations, Trending, and Photos 12 | * New endpoint `/users/me/channels/existing_pm` retrieves a channel between a given set of users, if it exists 13 | * The subscribed channels endpoint (`/users/me/channels/subscribed`) includes count for unread PMs in the meta field 14 | * Can now add "notes" to unused invites, to easily track what you're doing with them 15 | 16 | 17 | ### Changes 18 | 19 | * PM notifications are strictly indicators that you have unread PMs, and no longer contain the text of the messages 20 | * Better HTML version of the mention notification E-mail 21 | * Underscores now allowed in tags 22 | * Small improvement to threads and user profiles on pnut.io 23 | * Clients now require approval before being published to the app directory 24 | 25 | 26 | ### Fixes 27 | 28 | * Deauthorizing a client that doesn't have any tokens now deauthorizes its scopes properly 29 | * Deleted posts should no longer show up in older threads/streams (as well as other bleeding possibilities) 30 | * Channels were showing up multiple times (a subscription bug) on the subscribed channels endpoint 31 | * `count=-**n**` now works as expected on post streams 32 | * Text with E-mail addresses in it should not be parsed as links or mentions 33 | 34 | *Released 2017-03-03* -------------------------------------------------------------------------------- /changes/0.4.5b.md: -------------------------------------------------------------------------------- 1 | ## [v0.4.5b](https://pnut.io/docs/changes/0.4.5b) {#0.4.5b} 2 | 3 | This is a minor feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * "Support Us" page has basic stats 8 | * New "Activity" account page, which shows the last 10 significant actions made in your account (logins, password changes, etc.) 9 | 10 | *Released 2017-03-14* -------------------------------------------------------------------------------- /changes/0.5.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.5.0](https://pnut.io/docs/changes/0.5.0) {#0.5.0} 2 | 3 | This is a feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * App tokens 8 | * App Streams 9 | * Umlauts, emoji, foreign characters allowed in tags 10 | * Logo 11 | * Many new documents, some vague 12 | 13 | ### Changes 14 | 15 | * Redirect URIs can now have query parameters and still validate 16 | * Link entities will not include redundant phishing protection 17 | * API documentation is rearranged, and included verbatim from the GitHub repository 18 | * OAuth now warns when the client is requesting a non-HTTPS redirect_uri, and better displays what is being authorized 19 | * pnut.io now shows oembed images on posts. Other minor improvements 20 | * The "Support Us" page now includes message counts in the stats 21 | * Mentions in a post are now more likely to be considered "leading mentions" 22 | * Messages do not include counts.replies at all (for now) 23 | 24 | ### Fixes 25 | 26 | * Multiple manually submitted links would fail to be parsed 27 | * Invites were not calculated correctly (so were seldom given out) 28 | * Redirecting after login and related edge cases are improved around OAuth 29 | 30 | *Released 2017-04-15* -------------------------------------------------------------------------------- /changes/0.5.1.md: -------------------------------------------------------------------------------- 1 | ## [v0.5.1](https://pnut.io/docs/changes/0.5.1) {#0.5.1} 2 | 3 | This is a minor feature and bug fix update to pnut.io. 4 | 5 | ### Features 6 | 7 | * The first developer to authorize an extended scope can now customize a description of what it is used for, when users authorize the scope in the future ("Content Types" in dev area) 8 | * Users can change username once, and can change capitalization as often as they want, from pnut.io profile settings 9 | * Including `#nsfw` in a post automatically marks it as "Not Safe for Work", unless explicitly marked otherwise 10 | * Including `?exclude_channel_types=` on channel streams excludes the given types from the stream (opposite of `?channel_types=`) 11 | * Including `?include_limited_users=1` on subscribed channel streams and individual channel calls will include users as limited objects in the ACL instead of user IDs only 12 | * "Missed Conversations" explore stream added (random posts that had no interactions) 13 | 14 | ### Changes 15 | 16 | * Activity log now includes administrative events (when an admin makes you a developer, suspends your account, etc.) 17 | * Bare links are now parsed differently, and will accept more valid links, including IPv4/6 addresses 18 | 19 | ### Fixes 20 | 21 | * Emoji tag streams did not function on pnut.io 22 | * `/users/{user_id}/posts` now returns the user's posts even if an authorized user requests it after blocking or muting them 23 | 24 | *Released 2017-04-30* -------------------------------------------------------------------------------- /changes/0.6.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.6.0](https://pnut.io/docs/changes/0.6.0) {#0.6.0} 2 | 3 | This is a major feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * File API 8 | * Pay-what-you-want tier! 9 | * Repost and follow E-mail notifications 10 | * RSS feed of personal and unified streams 11 | * File Storage 12 | * MFA logins (TOTP option) 13 | * Added [microformats2](http://microformats.org/wiki/microformats2) to pnut.io posts and profiles 14 | * Added `markdown_text` to user profile when retrieving own profile (helpful for editing) 15 | * Including `&simple_login=1` on web authentication flows now shows an embed-friendly page for oauth 16 | 17 | 18 | ### Changes 19 | 20 | * Consolidated some account management areas 21 | * Increased invite limit from 5 to 7 22 | * Clarified activation process on new sign up 23 | * Notification E-mails are now digests 24 | * Avatars and cover images limited to 2097152 and 4194304 bytes, instead of 2000000 and 4000000 bytes 25 | * Posts from bots will not add threads to the "Conversations" explore stream 26 | * pnut.io now reverses markdown when editing your profile 27 | 28 | 29 | ### Fixes 30 | 31 | * Password recovery E-mail address was case-sensitive 32 | * "Users" count on user objects (number of users invited) was incorrect 33 | * Deleted messages were not being sent to App Streams 34 | * Channel owner access was denied in an edge case 35 | 36 | Note that after a year, current developers will have to pay $10/year for developer access. 37 | 38 | *Released 2017-07-25* -------------------------------------------------------------------------------- /changes/0.7.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.0](https://pnut.io/docs/changes/0.7.0) {#0.7.0} 2 | 3 | This is a major feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * User, post, channel, and message search 8 | * Derivative files 9 | * Auto-generated thumbnails for images 10 | 11 | 12 | ### Changes 13 | 14 | * File link expiration extended 15 | * `?include_directed_posts=0` does not exclude your own posts 16 | * Replies to your own posts do not add that thread to the Conversations explore stream 17 | * App streams allow the access token to be included in the header or query parameter, like other API calls 18 | * A message in a PM channel will re-subscribe any users who haven't muted the channel 19 | * Improved domain verification on profiles using `rel="me"` link 20 | * TOTP login authentication is available to everyone 21 | 22 | 23 | ### Fixes 24 | 25 | * `/users/{user_id}/clients` not retrieving all active clients in some cases 26 | * TOTP authentication setup 27 | * PM not able to find existing channel in some cases 28 | * Owners and full-ACL users not able to delete others' messages in non-PM channels 29 | * Complex tags in posts not rendered as links on pnut.io 30 | * `redirect_uri` did not work with an edge case 31 | 32 | *Released 2017-08-27* -------------------------------------------------------------------------------- /changes/0.7.1.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.1](https://pnut.io/docs/changes/0.7.1) {#0.7.1} 2 | 3 | This is a minor feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * Personal data exports may be requested in the "Data" section of your account (*Currently does not include files) 8 | * Numerous new characteristics for developers to note about their apps, including pictures 9 | * API errors will often include referenceable `meta.error_id` on 500 responses 10 | 11 | 12 | ### Changes 13 | 14 | * `/users/me/actions` now combines multiple objects in the `objects` list for the same action commited by different users 15 | * Front page and app pages redesigned 16 | * Link parsing should be operational; API will retrieve title and description for a link 17 | * Removed *all* GUID properties (posts, users, messages) 18 | 19 | 20 | ### Fixes 21 | 22 | * RSS tag feeds of multibyte character tags would not load 23 | 24 | *Released 2017-09-10* -------------------------------------------------------------------------------- /changes/0.7.2.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.2](https://pnut.io/docs/changes/0.7.2) {#0.7.2} 2 | 3 | This is a minor feature update to pnut.io. 4 | 5 | ### Features 6 | 7 | * App Directory reorganized into categories, multiple platforms 8 | * Apps can now be "recommended" by users 9 | * Post and message stream `meta` now includes `deleted_ids` and `revised_ids`, which are lists of post or message IDs that have been deleted or revised since `since_id` 10 | * pnut badges added to profiles; supporting users may opt in to include `badge: {object}` on their user object 11 | 12 | 13 | ### Changes 14 | 15 | * pnut.io front page redesign 16 | 17 | 18 | ### Fixes 19 | 20 | * `guid` was still included on some posts and users 21 | * Creating App Streams did not handle some errors clearly 22 | 23 | *Released 2017-09-30* -------------------------------------------------------------------------------- /changes/0.7.3.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.3](https://pnut.io/docs/changes/0.7.3) {#0.7.3} 2 | 3 | This is a minor update to pnut.io. 4 | 5 | 6 | ### Changes 7 | 8 | * Users can now sign up for an account with a pnut badge if they don't have an invite 9 | * E-mail notifications happen instantaneously, then follow up with a digest of more of the same soon after 10 | * Password flow will return error for invalid scopes 11 | * Can remove verified domain from profile 12 | * Invites page improved 13 | * Languages and timezones limited to finite list instead of dynamically generated list 14 | * Enabling MFA requires a TOTP code up front, before enabling 15 | 16 | 17 | ### Fixes 18 | 19 | * Password flow didn't allow extended scopes 20 | * `GET /users/me/channels` did not work with only extended scopes authorized 21 | * No longer authorizes an extended scope if its basic scope has already been authorized 22 | 23 | *Released 2017-10-14* -------------------------------------------------------------------------------- /changes/0.7.4.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.4](https://pnut.io/docs/changes/0.7.4) {#0.7.4} 2 | 3 | ### Features 4 | 5 | * App directory "early access" and "bot" categories 6 | * Tokens can be assigned a "token group" on creation, and tokens can be revoked for an app based on IP address and token group 7 | * `kind: audio` file type recognizes MP3, FLAC, and WAVE files 8 | * `email` scope has been added, which adds `email` to user tokens 9 | * `files:core_image` and `files:core_audio` special scopes have been added, giving access to their respective `kind` of files 10 | * oEmbed endpoint for posts and files 11 | * Endpoints for reporting posts and messages 12 | * `newcomers` post explore stream, including users' first 50 posts 13 | * Post, channel, and message searches allow `?raw_types` parameter 14 | * `io.pnut.core.channel.avatar` and `io.pnut.core.channel.cover` raw types 15 | 16 | ### Changes 17 | 18 | * RSS moved from https://pnut.io/feed/rss/ to https://api.pnut.io/v0/feed/rss/ (old links redirect) 19 | * Canonical posts and user profiles direct to @xyz's [beta app](https://beta.pnut.io) 20 | * Notifications are silenced within 30 seconds of previous notifications of the same type, and channel notifications are silenced if the user has a stream marker ahead of the message 21 | * Developers can "remember" pnut.io login 22 | 23 | ### Fixes 24 | 25 | * `raw` properly catches any improper values for `value` 26 | * Some cases where objects wouldn't be sent to app streams 27 | * User editing reverse markdown was broken with multiple markdown links 28 | * New developers' existing apps were not automatically made usable by other users 29 | * Bot and feed users' mentions did not correctly go to mention streams 30 | * `?include_message_html=0` was ignored 31 | 32 | *Released 2017-11-16* -------------------------------------------------------------------------------- /changes/0.7.5.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.5](https://pnut.io/docs/changes/0.7.5) {#0.7.5} 2 | 3 | ### Features 4 | 5 | * Pnut.io account area translations 6 | * New Privacy page includes mutes, blocks, and new function to limit who can create new private message channels with you 7 | * Bookmark E-mail notifications option for pnut badge supporters 8 | * App Streams support `raw` by setting appropriate query parameters on the websocket link 9 | 10 | ### Changes 11 | 12 | * Developer client "tokens" are no longer a thing; instead, new clients can be made by developers after a certain period 13 | * `kind` is no longer required on file upload; you may juggle whether to specify `kind`, `mime_type`, neither, both 14 | * `locale` limited to smaller list; more locales should be added on demand and as supportable 15 | * `email` scope is not authorizable over Password Flow, to prevent possible privacy concern 16 | * Link entity parsing adjusted (now includes links preceded by parenthesis) 17 | * Message streams now allow negative `count` (`?count=-4`) 18 | * App Streams now can differentiate between revised, reposted, and newly created or deleted posts 19 | 20 | ### Fixes 21 | 22 | * Numerous possible edge cases with app streams returning partial objects 23 | 24 | *Released 2017-11-30* -------------------------------------------------------------------------------- /changes/0.7.6.md: -------------------------------------------------------------------------------- 1 | ## [v0.7.6](https://pnut.io/docs/changes/0.7.6) {#0.7.6} 2 | 3 | This is a bug fix update to pnut.io. 4 | 5 | ### Features 6 | 7 | * Option to require OTP passwords for password flow authentications 8 | 9 | ### Changes 10 | 11 | * One-Time Passwords now create a short random string for a name, instead of user input 12 | * More clarity on profile update form 13 | * MFA login now allows "Remember login" cookie but requires the TOTP code. A failed attempt or leaving the page will unset the "Remember login" cookie. 14 | * Web Flows and Password Flow return much more specific feedback when something is not set properly 15 | 16 | ### Fixes 17 | 18 | * Password form validation required a digit and uppercase, which is not supposed to be required anymore 19 | * Failed MFA login would redirect to the normal login without client authorization details, if they had been set 20 | 21 | *Released 2017-12-10* -------------------------------------------------------------------------------- /changes/0.7.7.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * API Documentation app can be authorized, allowing you to make API calls from documentation and see the response live. The examples are editable on-page 4 | 5 | ### Changes 6 | 7 | * Link parsing improvements 8 | * Adjusted notification email timing 9 | * Post and message URI Templates must use `{object_id}` instead of `{post_id}` in links 10 | * Documentation includes type of access token required for endpoints (app vs user) 11 | 12 | ### Fixes 13 | 14 | * Failed login went to MFA login 15 | * `content.html` now begins with `` instead of `` to follow [microdata spec](http://schema.org/docs/gs.html#microdata_itemscope_itemtype) 16 | 17 | *Released 2018-01-14* -------------------------------------------------------------------------------- /changes/0.8.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.8.0](https://pnut.io/docs/changes/0.8.0) {#0.8.0} 2 | 3 | ### Features 4 | 5 | * Polls 6 | * New users are highlighted on the Invites page 7 | 8 | ### Changes 9 | 10 | * Emails on account lock out and new recent IP login 11 | * Password Flow authorization emails now express what new scopes are authorized 12 | * Basic stats are public 13 | * `avatar_image` included in channels when `?include_limited_users=1` query parameter is set 14 | * Links with emoji in the domain are recognized as links 15 | * @xyz's app *Beta* now uses the subdomain https://beta.pnut.io and is encouraged as users' first app 16 | 17 | ### Fixes 18 | 19 | * Text file encoding was ignored on upload 20 | * File names used the file token for its name when downloaded, instead of original file names 21 | * Some instabilities with database handling/what happened if there was a bug 22 | * Users could make their own posts trend 23 | 24 | *Released 2018-03-24* -------------------------------------------------------------------------------- /changes/0.9.0.md: -------------------------------------------------------------------------------- 1 | ## [v0.9.0](https://pnut.io/docs/changes/0.9.0) {#0.9.0} 2 | 3 | ### Features 4 | 5 | * User Streams (user-specific websockets, like App Streams for individual users) 6 | * Invited users now have 512MiB of free storage, ensuring everyone has some free storage 7 | * Users may gift Pnut Badges to other users 8 | * Basic poll search 9 | * Get multiple polls by ID in a single call 10 | * Bookmarks may be saved with `note`, which is only visible to the user who bookmarked it, when retrieving their own bookmarks 11 | * Clients can limit what scopes can be authorized 12 | * `/sys/stats` now includes `counts.clients.public`, tracking clients that are "active/public" and usable by more than just a single user 13 | * Alternative API domain https://pnut-api-1.org 14 | * App Streams messages and posts now include `meta.suppress_notifications`, and `meta.subscribed_user_ids` to simplify notifications 15 | * `include_replies` and `include_mention_posts` query parameters for post streams 16 | * `has_mentions` post search filter 17 | 18 | ### Changes 19 | 20 | * Requesting account deletion requires password verification 21 | * Only human account-types can respond to polls 22 | * `is_your_response` on poll options changed to `true` or `false` from `1` or `0` 23 | * App Streams now have connection- and subscription-level query parameters 24 | * App Streams objects are more consistent 25 | * User tokens return with `storage.total` in addition to `storage.available`. 26 | 27 | ### Fixes 28 | 29 | * Some old avatars were not deleted 30 | * `/users/{id}/cover` was not redirecting properly 31 | * "Account locked out" was logged after single failed login attempt 32 | * `include_user=0` returned empty string instead of user's ID for embedded users on file objects 33 | * Following a user in rapid succession could cause multiple listings of a user in your follows or followings 34 | 35 | *Released 2018-08-13* -------------------------------------------------------------------------------- /changes/0.9.1.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * Badge directory, earning, assigning, and setting by clients 4 | * Added `mime_types` filter to files 5 | * Added `+io.pnut.core.user` raw replacement value 6 | * Post search and channel message endpoints are accessible from RSS 7 | * New `poll_response` interaction when someone responds to your poll 8 | * New `/users/me/polls/responses` endpoint 9 | 10 | ### Changes 11 | 12 | * Improved documentation 13 | * Alternative `/users/me/interactions` and `/posts/{post_id}/interactions` endpoints 14 | * Tweaked post search 15 | * Small performance improvements to all POST calls 16 | 17 | ### Fixes 18 | 19 | * Audio file attached via oEmbed had faulty `url_expires_at` 20 | * Server-side flow didn't preserve `state` parameter 21 | * Could not delete polls 22 | * Deleted messages didn't send over user and app streams 23 | 24 | *Released 2018-09-13* -------------------------------------------------------------------------------- /changes/0.9.2.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * Allow `/text/process` to render messages instead of posts, with a query parameter 4 | * File `kind` can be inferred from file extension 5 | * `io.pnut.core.channel-invite` raw now attaches the related channel's `name`, if a "chat" channel 6 | * Markdown links with titles will show `title` on the object, not just embedded in `html` 7 | * Users can specify link template used in notification E-mails 8 | 9 | ### Changes 10 | 11 | * Improved documentation for searches, and templatized API example responses 12 | * Known file extensions will be normalized lowercase 13 | * Ellipsis (`…`) is ignored at the end of inline links 14 | * Improved duplicate post handling 15 | * Links handle parentheses more naturally 16 | * Repost and bookmark E-mail notifications include the post's text 17 | * Developer accounts are now a one-time fee of $42, instead of an annual subscription 18 | * Tags can still include underscores in text and html, but lookups ignore them 19 | 20 | ### Fixes 21 | 22 | * Account data page wouldn't show usage for non-Badge-holders 23 | * Newly invited users did not register following the inviter 24 | * oEmbed `raw` was accepting strings and non-Integer numbers for image width and height 25 | * Gifted Pnut badges weren't accepted for one case 26 | * Authorizing new client email included an empty client name 27 | * `redirect_uri`s with special schemes (not "https") weren't recognized 28 | * Post and message length was calculated with an approximation, now it uses the same calculation as in actual rendering 29 | * Post search failed for some lookup combinations 30 | 31 | *Released 2018-12-31* -------------------------------------------------------------------------------- /changes/0.9.3.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * E-mail "poll finished" notifications 4 | * JPEG images with EXIF data will be rotated, and EXIF data will be stripped 5 | * `GET /clients/{id}` includes the client's logo, if it has one 6 | * `io.pnut.core.chat`-type message RSS feeds include the channel's name in the title and description 7 | 8 | ### Changes 9 | 10 | * Poll responses can be changed until a poll closes 11 | * Any member of a private message can sticky messages for the group 12 | * E-mail notifications always include "canonical" links, even if custom links are set in notification settings 13 | * `suppress_notifications` on App Streams now includes muted users 14 | 15 | ### Fixes 16 | 17 | * Error updating user badges 18 | 19 | *Released 2019-04-07* -------------------------------------------------------------------------------- /changes/0.9.4.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * `GET /sys/stats` includes `data.counts.users.today`, which is unique account IDs accessed in the current UTC day 4 | * Entities now include `gopher://` links in addition to `http://`, `https://`, and `ftp://` 5 | * Polls can optionally allow users to select multiple options, up to `max_options` 6 | * Channel search can be ordered by `popularity`--how many messages have been made in the channel 7 | * Channel search includes a basic text query of chat room names and descriptions 8 | * `io.pnut.core.fallback_url` added to `raw` elements 9 | 10 | ### Changes 11 | 12 | * Reposts include their reposted posts' `raw` 13 | * `GET /users/{id}/presence` and `GET /presence` return limited users including avatar image, username, name 14 | * `GET /token` now includes `markdown_text` 15 | * Account badge and payment pages have been reorganized 16 | 17 | ### Fixes 18 | 19 | * E-mail notifications for polls closing occurred regardless of notification setting 20 | * Notification custom links not recognized 21 | * Unmuting from Pnut.io 22 | * First-time authorization after TOTP login failing to return Oauth state 23 | * Post search and user file retrieval edge cases 24 | * Documentation includes examples for user streams 25 | * `markdown_text` on `GET /users/me` parsed some links improperly 26 | 27 | *Released 2019-08-03* -------------------------------------------------------------------------------- /changes/0.9.5.md: -------------------------------------------------------------------------------- 1 | ### Features 2 | 3 | * New `video` file standardization, on file uploads and oembeds 4 | * Revert user images to defaults (`DELETE /users/me/cover` and `DELETE /users/me/avatar`) 5 | * Invites now include a link to a QR code image of the invite, in addition to the copyable link and E-mail options 6 | * When authorizing an app, users can directly uncheck requested permissions they do not want to allow 7 | * The website defaults to light-mode, and uses CSS preferences for showing dark-mode 8 | * Channel Search endpoint as RSS feed 9 | 10 | ### Changes 11 | 12 | * `io.pnut.core.crosspost`-type raw items expanded with options for external user representation 13 | * `embeddable_url` in oEmbeds now can use template URIs (`{object_id}`) 14 | * `max_options` on polls can now be the total number of options, instead of one less than the total 15 | * File `name` can be inferred from the uploaded file's name, if not included in the POST 16 | * When a user no longer has any access tokens for an app, the app's scopes will be revoked, and the user will have to re-authorize scopes the next time a token is created 17 | * Sending a message to User or App streams (websockets) will return a "pong" response 18 | * User and App streams require a "ping" every 60 seconds, instead of every 50 seconds 19 | 20 | ### Fixes 21 | 22 | * Personal data export files in zip file are now encoded properly for UTF-8 23 | * `https://photos.pnut.io/{post_id}/{image_placement_order}` and `https://photos.pnut.io/{file_id}` oembed links now temporarily redirect to post threads on Beta 24 | * Poll notifications could fire multiple times for a single participant 25 | * Saving Invite notes redirected to a 404 26 | * Upgrading to developer account failed 27 | * Some complex channel searches would fail 28 | 29 | *Released 2020-01-01* -------------------------------------------------------------------------------- /changes/0.9.6.md: -------------------------------------------------------------------------------- 1 | *Released on * 2 | 3 | ### Features 4 | 5 | __Used invites view__ User 6 | 7 | In your account, you can now see your invites that have been used, from https://pnut.io/account/used_invites. 8 | 9 | 10 | __Simple QR code invite__ User 11 | 12 | You can easily pull up a *random*, unused invite as a QR code, by clicking the button "Grab an Invite" at the top right of the [Invites](https://pnut.io/account/invites) page in your account, or by going directly to https://pnut.io/account/invite_8. This could be helpful when you want to share an invite with a friend or acquaintance in-person. 13 | 14 | 15 | __Chat room E-mail notifications__ User 16 | 17 | Enabled in your account under [Notifications](https://pnut.io/account/notifications), you can receive E-mails from Pnut for chat room activity like you already may receive Private Message notifications. 18 | 19 | 20 | __RSS feed URI templates__ 21 | 22 | Now [RSS feeds](https://pnut.io/docs/resources/other/rss#template-uris) can use URI templates to create app-specific links to messages and posts from the feeds. Previously, a user's feed reader would always link them to a message or post on https://beta.pnut.io. This worked for posts, but if you had a custom channel type, it never made sense to send a reader to Beta, because Beta can only understand chat room- and private message-type channels. 23 | 24 | 25 | __MFA backup code__ User 26 | 27 | Now when adding multi-factor authentication to your account login, under account [Security](https://pnut.io/account/security#mfa), you will be given a backup code that can be used to disable MFA on your account, in case your device becomes damaged or inaccessible. 28 | 29 | 30 | __E-mail notification link presets__ User 31 | 32 | Under account [Notifications](https://pnut.io/account/notifications#advanced), you can choose to link to a specific client for a mention, PM, or chat activity. By default, Pnut's E-mails link to the Beta app. But you could choose to link to open in ChimPnut, for example. 33 | 34 | Now those E-mail notifications have a handful of preset "clients" to choose from, in addition to the custom options. (If you are developing an application and would like yours included in the presets, contact support.) 35 | 36 | 37 | 38 | ### Changes 39 | 40 | 41 | __"API changes" documentation__ 42 | 43 | Now API releases have their own pages with more fleshed-out descriptions of changes, instead of only one-liners. 44 | 45 | 46 | __Markdown link length calculation documentation__ 47 | 48 | Markdown link length is somewhat nuanced, so its calculation has been [added to the API documentation](https://pnut.io/docs/implementation/entities#markdown-links). Nothing is changing here, but it is worth calling out in case your client is handling it improperly or you were curious. 49 | 50 | 51 | __Marking all of a channel type as "read"__ 52 | 53 | * `GET /users/me/channels/num_unread/pm` 54 | * `DELETE /users/me/channels/num_unread/pm` 55 | * [`GET /users/me/channels/num_unread`](https://pnut.io/docs/resources/channels/lookup#delete-users-me-channels-num_unread) 56 | * [`DELETE /users/me/channels/num_unread`](https://pnut.io/docs/resources/channels/lookup#delete-users-me-channels-num_unread) 57 | 58 | The two unread PM endpoints were for getting a count of unread private messages or marking all PMs as "read". Those two endpoints still exist but are being deprecated in favor of a generic endpoint for getting any channel types' unread counts, and marking any channel types' channels as "read". Two new endpoints have been added to be used to mark any channel type as "read" with the query parameter `?channel_types` and a CSV of up to 10 of the channel types desired. 59 | 60 | 61 | __Unread chat room count on subscribed channels__ 62 | 63 | * [`GET /users/me/channels/subscribed`](https://pnut.io/docs/resources/channels/subscribing#get-users-me-channels-subscribed) 64 | 65 | The call to a user's subscribed channels now includes `meta.unread_counts.io.pnut.core.chat`, making it easier for a client to know if there are unread chat rooms. 66 | 67 | 68 | __Message search "replies" flag__ 69 | 70 | * [`GET /channels/messages/search`](https://pnut.io/docs/resources/messages/search) 71 | 72 | The message search endpoint used to ignore the `?is_reply` query parameter unless it was set to `1` (true). 73 | 74 | Now this parameter will respect `?is_reply=0` or `?is_reply=1`, but still be ignored if it is unset or `?is_reply=`. You may look for messages that are not replies, now. If your client has been setting this parameter to `0` when not in use, it may have unexpected results. 75 | 76 | This behavior for booleans in the API, to be set true, false, or not set, will likely spread in future releases. 77 | 78 | 79 | __More flexible user messages scopes__ 80 | 81 | * [`GET /users/me/messages`](https://pnut.io/docs/resources/messages/lookup#get-users-me-messages) 82 | 83 | The user messages endpoint no longer requires full `messages` or `public_messages` scopes, but can use extended scopes like most other messages endpoints. 84 | 85 | 86 | ### Fixes 87 | 88 | __Localizations__ 89 | 90 | Localization files are live again on Pnut.io, and contributors are updating their files. Thanks to [@akr](https://pnut.io/@akr), [@ericd](https://pnut.io/ericd), [@hutattedonmyarm](https://pnut.io/@hutattedonmyarm) for translations! 91 | 92 | 93 | __Poll search order__ 94 | 95 | * [`GET /polls/search`](https://pnut.io/docs/resources/polls/search) 96 | 97 | When searching for polls, the API had options for ordering results in descending order by `id` or by `closed_at`, but `closed_at` actually was expecting the query parameter `created_at`. 98 | -------------------------------------------------------------------------------- /changes/1.1.0.md: -------------------------------------------------------------------------------- 1 | *Released on * 2 | 3 | 4 | 5 | 6 | ### Features 7 | 8 | #### "Welcoming committee" option for email digests 9 | 10 | In your [account notifications](https://pnut.io/account/notifications), you can now enable regular digest E-mails that include "New users and posts from infrequent users". There was already an option to receive similar posts from people you follow, but now you can receive notices for posts from people you don't follow. 11 | 12 | #### User followers and following can be ordered by last_post_id, followed_at, id 13 | 14 | Sorting following lists can help you find more relevant users more easily. Could be interesting to pull up folks you haven't heard from recently, or sort by when they joined the network, for example. 15 | 16 | #### User search can filter by is_following, is_follower 17 | 18 | Limiting searches to `is_following=0` will only show users you aren't already following, while `is_follower=1` would only show users that do follow you. 19 | 20 | #### New "256" post explore stream 21 | 22 | A Post explore stream has been added which only shows posts that are 256 characters long. 23 | 24 | 25 | ### Fixes 26 | 27 | #### Personal Data Request Broken on v1 28 | 29 | Fixed 30 | 31 | #### Not all Chat Room HTML Converted to Entities 32 | 33 | Fixed 34 | 35 | #### Changing account to bot does not properly register it as bot 36 | 37 | A few endpoints still recognized users as "human"-type. 38 | 39 | #### Changing account to feed left some follower data intact 40 | 41 | Now properly handles removing followers. 42 | 43 | #### Developers could not create new clients 44 | 45 | Client creation restored. 46 | 47 | #### V0 Poll raw was showing in new v1 format 48 | 49 | Reworked to show old style when pulling up from V0. 50 | 51 | #### Channel Subscribers sometimes showing duplicates 52 | 53 | Now only one of a subscriber will show. -------------------------------------------------------------------------------- /changes/1.2.0.md: -------------------------------------------------------------------------------- 1 | *Released on * 2 | 3 | 4 | 5 | 6 | ### Features 7 | 8 | #### Suggested Users 9 | 10 | * `GET /users/suggested` 11 | 12 | This new endpoint provides a simple jumping off point for finding users. It's not complex. Hopefully works for users with no followers and users with some followers. 13 | 14 | #### User Presence in app streams and user streams 15 | 16 | The `presence` scope can be requested by a user or app stream, showing instant updates to users' presences. 17 | 18 | #### Files include lists of objects they are attached to 19 | 20 | Now file objects include a list of the messages, polls, and posts that they have been attached to any, under `attached_to`. This helps determine what would be impacted if you deleted the file, and track down how you've used a file. 21 | 22 | - https://docs.pnut.io/resources/files#file-fields 23 | 24 | #### Exclude users from post search 25 | 26 | When searching posts, you can provide a list of user IDs to the query field `exclude_user_ids` to exclude from the results. This could be useful if a particular user posts a lot about something and you want to see anyone else, or if you want to exclude yourself from a search. 27 | 28 | - https://docs.pnut.io/resources/posts/search#get-posts-search 29 | 30 | #### Chat room names in E-mail digests 31 | 32 | E-mail digest notifications will now include chat room names, if the channel is a chat room-type. 33 | 34 | Manage them from https://pnut.io/account/notifications#digest. 35 | 36 | 37 | ### Changes 38 | 39 | #### Follower data in user export 40 | 41 | Exporting your data now includes your followers and following users. 42 | 43 | 44 | ### Fixes 45 | 46 | #### Stickying message without authentication returned nothing 47 | 48 | Now stickying a message without authentication will return a proper error message. 49 | 50 | #### User clients count missing on App Streams 51 | 52 | When a User object came across an App Stream, it did not include `counts.clients`. 53 | 54 | #### Bitrate field for Audio file raw 55 | 56 | Audio files embedded in another object's `raw` data did not properly reference its bitrate. 57 | 58 | #### Some calls not finding scopes when calling unauthenticated 59 | 60 | Sometimes calling an endpoint that required authentication would fail and not explain itself. 61 | 62 | #### V0 File Delete returns 404 63 | 64 | Now it returns the deleted file info as expected. 65 | 66 | #### V0 User Presence update response not consistent 67 | 68 | It was returning unexpected objects. 69 | -------------------------------------------------------------------------------- /changes/1.3.0.md: -------------------------------------------------------------------------------- 1 | *Released on * 2 | 3 | 4 | 5 | 6 | ### Features 7 | 8 | #### Per-Channel User Presence 9 | 10 | * `GET /channels/{channel_id}/presence` 11 | * `GET /users/{user_id}/presence/channels/{channel_id}` 12 | * `PUT /users/me/presence/channels/{channel_id}` 13 | 14 | A user's presence can be set for a specific channel, separately from its global User Presence. You can have per-channel statuses, or overload the functionality for an ephemeral chat. 15 | 16 | * https://pnut.io/docs/resources/channels/presence 17 | 18 | 19 | #### User Presence timeout configurable 20 | 21 | * `PUT /users/me/presence` 22 | * `PUT /users/me/presence/channels/{channel_id}` 23 | 24 | You can now set the expiration for a User Presence or Channel User Presence, up to 7 days, by including a timestamp for field `expiration`. 25 | 26 | * https://pnut.io/docs/resources/users/presence#put-users-me-presence 27 | 28 | 29 | #### Delete multiple files at once 30 | 31 | * `DELETE /files?ids=1,2,3` 32 | * `GET /sys/ops/{uuid}` 33 | 34 | The bulk file delete endpoint can delete up to 200 files at once. Because many files may be involved (and derivative files!), the endpoint will just return the File IDs it is deleting, and an endpoint you can poll for status of the job, in `meta.status_url`. 35 | 36 | * https://pnut.io/docs/resources/files/lifecycle#delete-files 37 | 38 | 39 | 40 | ### Fixes 41 | 42 | #### Revising Post not checking entities 43 | 44 | A revised post must retain the same links, mentions, and tags as the original post. Revising a post was not properly guarding against them changing. 45 | 46 | #### Post search by `raw_types` failing 47 | 48 | 500 error. 49 | 50 | #### Data export file name referred to wrong username 51 | 52 | The ZIP file would have a different username on it. 53 | 54 | #### V0 System Stats endpoint failing 55 | 56 | 500 error. 57 | 58 | #### V0 Client link entities wrong 59 | 60 | On v0 API calls, clients would refer to `url` instead of `link` in some cases. 61 | -------------------------------------------------------------------------------- /how-to/channel-permissions.md: -------------------------------------------------------------------------------- 1 | # How To Manage Channel Permissions 2 | 3 | Channels have three groups that determine who can read, write, and manage the channel: `full`, `write`, `read`. 4 | 5 | ```json 6 | "acl":{ 7 | "full":{ 8 | "immutable":false, 9 | "you":true, 10 | "user_ids":[] 11 | }, 12 | "write":{ 13 | "any_user":true, 14 | "immutable":false, 15 | "you":true, 16 | "user_ids":[] 17 | }, 18 | "read":{ 19 | "any_user":true, 20 | "immutable":false, 21 | "public":false, 22 | "you":true, 23 | "user_ids":[] 24 | } 25 | } 26 | ``` 27 | 28 | Users can't be in multiple groups. The higher-access groups inherit the lower functionality as well; someone who can manage a channel can write and read, and someone who can write, can read. 29 | 30 | The user who creates the channel automatically has `full` access, but is the `user` in the channel object, not in the ACL proper. The owner can be changed later. 31 | 32 | 33 | ## Permissions 34 | 35 | ### user 36 | 37 | * Deactivate the channel 38 | * Edit `full` access group 39 | * Change the owner of the channel 40 | 41 | 42 | ### full 43 | 44 | * Edit the channel and the `read` and `write` ACLs 45 | * Delete any messages from the channel 46 | 47 | 48 | ### write 49 | 50 | * Write messages to the channel 51 | 52 | 53 | ### read 54 | 55 | * Read and subscribe to the channel and messages in it 56 | 57 | 58 | ## Immutability 59 | 60 | By default, all ACLs are "mutable". They can change. Once set "*im*mutable", they can no longer be changed. 61 | 62 | The exception to this is `read.any_user`, which will still inherit `write.any_user` changing to `true` even if `read.immutable: true`. 63 | 64 | When you create a channel, be sure not to make an ACL immutable if you think you will want to change it later. -------------------------------------------------------------------------------- /how-to/channels-acl.md: -------------------------------------------------------------------------------- 1 | # How To: ACL 2 | 3 | Channels have three groups that determine who can read, write, and manage the channel. Users can't be in multiple groups. The higher-access groups inherit the lower functionality as well; someone who can manage a channel can write and read, and someone who can write, can read. The user who creates the channel automatically and irrevocably has `full` access, but is the `owner` user in the channel object, not in the ACL proper. 4 | 5 | 6 | ```json 7 | "acl":{ 8 | "full":{ 9 | "immutable":false, 10 | "you":true, 11 | "user_ids":[] 12 | }, 13 | "write":{ 14 | "any_user":true, 15 | "immutable":false, 16 | "you":true, 17 | "user_ids":[] 18 | }, 19 | "read":{ 20 | "any_user":true, 21 | "immutable":false, 22 | "public":false, 23 | "you":true, 24 | "user_ids":[] 25 | } 26 | } 27 | ``` 28 | 29 | ## Permissions 30 | 31 | ### owner 32 | 33 | * Deactivate the channel. 34 | * Edit `full` access group. 35 | 36 | 37 | ### full 38 | 39 | * Edit the channel and the `read` and `write` ACLs 40 | * Delete any messages from the channel. 41 | 42 | 43 | ### write 44 | 45 | * Write messages to the channel. 46 | 47 | 48 | ### read 49 | 50 | * Read and subscribe to the channel and messages in it. 51 | 52 | 53 | ## Immutability 54 | 55 | By default, all ACLs are "mutable". They can change. Once set "*im*mutable", they can no longer be changed. The exception to this is `any_user` on `read`. If `any_user` on `write` changes from false to true, `read`'s `any_user` will still inherit it. 56 | 57 | When you create a channel, be sure not to make an ACL immutable if you think you will want to change it later. -------------------------------------------------------------------------------- /how-to/channels-pm.md: -------------------------------------------------------------------------------- 1 | # How To: Private Message 2 | 3 | Private messages are special channels with the restricted `io.pnut.core.pm` channel type. They cannot be directly created or deactivated, and they're completely immutable (you cannot change who is included in the channel). This makes the "owner" of the channel irrelevant. 4 | 5 | 6 | ## Creation 7 | 8 | Channel creation is handled by Pnut. 9 | 10 | 11 | ## Sending a Message 12 | 13 | To send a message to other users, you need to know all of the users you will be sending the message to, or the ID of an already-created channel. If you know the channel, you may create a message in it just like you would any other channel. If you do not, or it has not been created yet, you can create an `application/json`-encoded message with the `channel_id` of "pm", and a special list in the JSON of `destinations`, listing all users by "@username" or their user ID. 14 | 15 | ```bash 16 | curl "https://api.pnut.io/v0/channels/pm/messages" \ 17 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 18 | -H "Content-Type: application/json" \ 19 | -d "{ 20 | \"text\":\"We should make great things.\", 21 | \"destinations\":[ 22 | 1,5,\"@ericd\" 23 | ] 24 | }" \ 25 | -X POST 26 | ``` 27 | 28 | In every other way, they can be treated like other channels and messages. Note that the "owner's" ID will not be in the write-access ACL, but they will not function any differently from anyone else. 29 | 30 | 31 | ## Existing PM Channels 32 | 33 | If you want to see a PM channel between specific users without sending a message, you can use the [Existing PM endpoint](../resources/channels/lookup#get-users-me-channels-existing_pm) to find the channel ID. 34 | 35 | 36 | ## ACL 37 | 38 | ```json 39 | "acl":{ 40 | "full":{ 41 | "immutable":true, 42 | "you":true, 43 | "user_ids":[] 44 | }, 45 | "write":{ 46 | "any_user":false, 47 | "immutable":true, 48 | "you":true, 49 | "user_ids":[1,5,20] 50 | }, 51 | "read":{ 52 | "any_user":false, 53 | "immutable":true, 54 | "public":false, 55 | "you":true, 56 | "user_ids":[] 57 | } 58 | } 59 | ``` -------------------------------------------------------------------------------- /how-to/chat-rooms.md: -------------------------------------------------------------------------------- 1 | # How To Create a Chat Room 2 | 3 | Chat rooms are a special core Pnut channel type, `io.pnut.core.chat`. They can be searched by category in addition to the ways other channels can be searched, and also have first-party E-mail notification support. 4 | 5 | * [Channel type](https://github.com/pnut-api/object-metadata/blob/master/channel-types/io.pnut.core.chat.md) 6 | 7 | The channel will have to be created with the `io.pnut.core.chat-settings` raw item. 8 | 9 | * [Chat Settings type](https://github.com/pnut-api/object-metadata/blob/master/raw/io.pnut.core.chat-settings.md) 10 | 11 | ### JSON Example Channel with Raw Item 12 | 13 | ```json 14 | { 15 | "type": "io.pnut.core.chat", 16 | "acl": { 17 | "write": { 18 | "any_user": true 19 | } 20 | }, 21 | "raw": { 22 | "io.pnut.core.chat-settings": [ 23 | { 24 | "name": "Frederick's Music Lounge", 25 | "description": { 26 | "text": "A musical interlude for music-lovers" 27 | }, 28 | "categories": ["community"] 29 | } 30 | ] 31 | } 32 | } 33 | ``` 34 | 35 | Returns a channel object with 36 | 37 | ```json 38 | { 39 | "type": "io.pnut.core.chat", 40 | ... 41 | "raw": { 42 | "io.pnut.core.chat-settings": [ 43 | { 44 | "name": "Frederick's Music Lounge", 45 | "description": { 46 | "entities": { 47 | "links": [], 48 | "mentions": [], 49 | "tags": [] 50 | }, 51 | "html": "A musical interlude for music-lovers", 52 | "text": "A musical interlude for music-lovers" 53 | }, 54 | "categories": ["community"] 55 | } 56 | ] 57 | } 58 | } 59 | ``` 60 | 61 | 62 | More options for the raw item are found in the community git repository linked above. 63 | 64 | Creating a chat room is executed like any other channel, with [POST /channels](../resources/channels/lifecycle#post-channels). 65 | 66 | ### Optional Improvements to Rooms 67 | 68 | #### Image for the Room 69 | 70 | Images representing the room may be added with the [avatar image](https://github.com/pnut-api/object-metadata/blob/master/raw/io.pnut.core.channel.avatar.md) and [cover image](https://github.com/pnut-api/object-metadata/blob/master/raw/io.pnut.core.channel.cover.md). 71 | 72 | #### Broadcast to Global 73 | 74 | Clients often allow users to "broadcast" a message from a public channel to the Global post stream. 75 | 76 | To do that, create a post with a [channel invite](https://github.com/pnut-api/object-metadata/blob/master/raw/io.pnut.core.channel.invite.md) attached, referencing the channel ID. 77 | 78 | Then create an identical message in the channel, with a [broadcast notice](https://github.com/pnut-api/object-metadata/blob/master/raw/net.patter-app.broadcast.md) on it referencing the ID of the post that was just created. 79 | 80 | #### -------------------------------------------------------------------------------- /how-to/dynamic-polling.md: -------------------------------------------------------------------------------- 1 | # How To Dynamically Update Polling Streams 2 | 3 | When polling a stream of posts like the global feed, or messages in a channel, there is a way to catch updates on deleted and revised posts. 4 | 5 | When polling using `since_id` pagination, the `meta` object on the stream will include lists `meta.deleted_ids` and `meta.revised_ids` if a post has been deleted or revised after the ID of `since_id` was created. 6 | 7 | For example: my client is making the request `curl "https://api.pnut.io/v1/posts/streams/global?since_id=500"`, when `500` is the latest post in the stream. Someone deletes their post `244`. The next time my client requests global with `since_id=500`, this is what I get back: 8 | 9 | ```json 10 | { 11 | "meta": { 12 | "more": false, 13 | "code": 200, 14 | "deleted_ids": [ 15 | "244" 16 | ] 17 | }, 18 | "data": [] 19 | } 20 | ``` 21 | 22 | Knowing this, my client can determine if post `244` is being displayed, and update its view to show that the post is now deleted. 23 | 24 | The same is true for revised posts, and deleted messages. The client, knowing that a post is revised, could automatically make a call to `GET /posts?ids=244`, for whatever posts were revised that were also being displayed. 25 | 26 | This is most useful when a client is polling forward, though; note that the sample of deleted and revised posts is not capped going forward. If you made a call with `since_id=200` and 40 posts had been deleted since post `200` was created, you would get all of those IDs. So it is not very useful when a client is pulling up old, cached posts (a fresh request of old posts would include the deleted or revised posts' most recent state, rendering "updates" like these irrelevant). -------------------------------------------------------------------------------- /how-to/overview.md: -------------------------------------------------------------------------------- 1 | # How To 2 | 3 | Here be explanations of dragons. 4 | 5 | If there is an area of the API that usually has an expected or common pattern, or a place that is particularly tricky to get right, we will write an explanation as a How To. 6 | 7 | These are what we have so far: 8 | 9 | -------------------------------------------------------------------------------- /how-to/private-messages.md: -------------------------------------------------------------------------------- 1 | # How To Use Private Messages 2 | 3 | Private messages are created in special channels with the restricted `io.pnut.core.pm` channel type. This is a list of ways they are different from normal channels: 4 | 5 | * They cannot be directly created or deactivated 6 | * They're always immutable (you cannot change who is included in the channel) 7 | * There is no `user`/owner, only users with `write` access 8 | * Any user can "sticky" a message 9 | 10 | 11 | ## Channel Creation 12 | 13 | Pnut automatically creates the channel when a private message is sent. If there has already been a message between the same parties previously, Pnut reuses the existing channel. 14 | 15 | 16 | ## Sending a Message 17 | 18 | To send a message to other users, you need to know all of the users you will be sending the message to, or the ID of an already-created channel. If you know the channel ID, you may create a message in it just like you would any other channel. If you do not, or it has not been created yet, you can create an `application/json`-encoded message with the `channel_id` of `pm`, and a special list in the JSON of `destinations`, listing all users by "@username" or their user ID. 19 | 20 | Here, the destinations are for user IDs `1` and `5`, and username `@ericd`: 21 | 22 | ```bash 23 | curl "https://api.pnut.io/v1/channels/pm/messages" \ 24 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 25 | -H "Content-Type: application/json" \ 26 | -d "{ 27 | \"text\":\"We should make great things.\", 28 | \"destinations\":[ 29 | 1,5,\"@ericd\" 30 | ] 31 | }" \ 32 | -X POST 33 | ``` 34 | 35 | This is what the example's channel permissions would look like if User ID `2` created the message: 36 | 37 | ```json 38 | "acl":{ 39 | "full":{ 40 | "immutable":true, 41 | "you":false, 42 | "user_ids":[] 43 | }, 44 | "write":{ 45 | "any_user":false, 46 | "immutable":true, 47 | "you":true, 48 | "user_ids":[1,2,5,20] 49 | }, 50 | "read":{ 51 | "any_user":false, 52 | "immutable":true, 53 | "public":false, 54 | "you":true, 55 | "user_ids":[] 56 | } 57 | } 58 | ``` 59 | 60 | The creating user's ID will be in the write-access ACL, just like everyone else. 61 | 62 | In every other way, they can be treated like other channels and messages. 63 | 64 | 65 | ## Existing PM Channels 66 | 67 | If you want to see a PM channel between specific users without sending a message, you can use the [Existing PM endpoint](../resources/channels/lookup#get-users-me-channels-existing_pm) to find the channel ID. -------------------------------------------------------------------------------- /implementation/libraries.md: -------------------------------------------------------------------------------- 1 | # Libraries 2 | 3 | Libraries are easy ways to integrate with the Pnut API from your language of choice. We will link to any we know from here, as well as other Pnut-specific coding tools. 4 | 5 | 6 | ### C# 7 | 8 | Name|Description|Maintainer|Source 9 | -|-|-|- 10 | pnutdotnet|library|[@dmoeller](https://pnut.io/@dmoeller)|[GitHub](https://github.com/dwmoeller1/pnutdotnet) 11 | 12 | 13 | ### Elixir 14 | 15 | Name|Description|Maintainer|Source 16 | -|-|-|- 17 | [ex_pnut](https://hex.pm/packages/ex_pnut)|library|[@lechindianer](https://pnut.io/@lechindianer)|[GitHub](https://github.com/Lechindianer/ex_pnut) 18 | 19 | 20 | ### Go 21 | 22 | Name|Description|Maintainer|Source 23 | -|-|-|- 24 | woodstock|library|[@thrrgilag](https://pnut.io/@thrrgilag)|[GitLab](https://gitlab.com/thrrgilag/woodstock) 25 | 26 | 27 | ### Javascript/Node 28 | 29 | Name|Description|Maintainer|Source 30 | -|-|-|- 31 | [pnut-butter.js](https://www.npmjs.com/package/pnut-butter)|library|[@kwood](https://pnut.io/@kwood)|[GitHub](https://github.com/kaiwood/pnut-butter) 32 | pnut.js|library|-|[GitHub](https://github.com/pnut-api/pnut.js) 33 | [pnut-to-markdown](https://www.npmjs.com/package/pnut-to-markdown)|package to convert HTML content back to raw input text (pre-markdown rendering)|[@kwood](https://pnut.io/@kwood)|[GitHub](https://github.com/kaiwood/pnut-to-markdown) 34 | stpnut|module for monitoring an App Stream|[@shawn](https://pnut.io/@shawn)|[GitHub](https://github.com/shawnthroop/stpnut) 35 | [passport-pnut](https://www.npmjs.com/package/passport-pnut)|Passport.js strategy|[@33MHz](https://pnut.io/@kwood)|[GitHub](https://github.com/33mhz/passport-pnut) 36 | 37 | 38 | ### PHP 39 | 40 | Name|Description|Maintainer|Source 41 | -|-|-|- 42 | [phpnut](https://packagist.org/packages/pnut-api/phpnut)|library|[@33MHz](https://pnut.io/@33mhz)|[GitHub](https://github.com/pnut-api/phpnut) 43 | 44 | 45 | ### Python 46 | 47 | Name|Description|Maintainer|Source 48 | -|-|-|- 49 | [PNUTpy](https://pypi.org/project/pnutpy/)|library|[@thrrgilag](https://pnut.io/@thrrgilag)|[GitHub](https://github.com/pnut-api/PNUTpy) 50 | 51 | 52 | ### Ruby 53 | 54 | Name|Description|Maintainer|Source 55 | -|-|-|- 56 | [pnut](https://rubygems.org/gems/pnut)|library|[@kwood](https://pnut.io/@kwood)|[GitHub](https://github.com/kaiwood/pnut-ruby) 57 | 58 | 59 | ### Swift 60 | 61 | Name|Description|Maintainer|Source 62 | -|-|-|- 63 | Peanut|library|[@shawn](https://pnut.io/@shawn)|[GitHub](https://github.com/shawnthroop/Peanut) 64 | PnutKit|library|[@paulyhedral](https://pnut.io/@paulyhedral)|[GitHub](https://github.com/exsortis/PnutKit) 65 | PnutAttributedString|handling entity positioning|[@ericd](https://pnut.io/@ericd)|[GitHub](https://github.com/ericdke/PnutAttributedString) 66 | -------------------------------------------------------------------------------- /implementation/overview.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | __Current Version: 1.3.0 [(Changes)](../changes)__ 4 | 5 | API Root: https://api.pnut.io/v1 6 | 7 | *Last Updated 2023-10-21* 8 | 9 | Welcome to the documentation for the Pnut API. 10 | 11 | __[Become a Developer](https://pnut.io/dev)__ 12 | 13 | If this is your first time here, it is recommended that you become familiar with the *Implementation* section and read through *Authentication* to determine what scopes and strategy you will want to use. 14 | 15 | The *How To* section has some examples and explanation of some common situations. 16 | 17 | 18 | #### Interactive Examples 19 | 20 | cURL examples can be edited on-page (they have the HTML5 `contenteditable` property). To call the API from the docs and see the cURL responses live, you must log in and authorize the API Documentation app linked to from the examples. The API Documentation app doesn't work with a few PUT, PATCH, and POST examples because of their complexity. 21 | 22 | 23 | ## External Resources 24 | 25 | ##### [Developer Chat](https://patter.chat/18) 26 | 27 | You are welcome to talk over the API in the Developer Channel. Questions, requests, floating ideas--any development-related conversation is encouraged! 28 | 29 | 30 | ##### [Libraries](libraries) 31 | 32 | List of libraries and code to easily interact with the API from various languages. 33 | 34 | 35 | ##### [API Documentation](https://github.com/pnut-api/api-spec) 36 | 37 | GitHub has a mirror of our API documentation. That is the recommended way to formally submit bugs and feature requests. 38 | 39 | 40 | ##### [Object Metadata](https://github.com/pnut-api/object-metadata) 41 | 42 | A repository for object metadata to coordinate common channel and Raw types. 43 | 44 | 45 | ##### [@testuser](https://pnut.io/docs/testuser) 46 | 47 | If you are developing an app for iOS, the @testuser account can be used for Apple Store reviewing. 48 | 49 | 50 | ##### Backup API Root 51 | 52 | There is a backup API Root at https://api.pnut-api-1.org/v1. The same subdomains and endpoints are available with this domain. DNS issues are known to occur (especially internationally), and if one does not work, the other may. 53 | -------------------------------------------------------------------------------- /implementation/pagination.md: -------------------------------------------------------------------------------- 1 | # Pagination 2 | 3 | 4 | Paginated calls will include a `pagination_id` on the paginated objects. 5 | 6 | The `meta` object in the returned JSON will also have a `min_id` and `max_id`. 7 | 8 | Objects are always returned in reverse chronological order (newest first). 9 | 10 | Pagination parameters can be used in conjunction with [stream markers](../resources/stream-marker) on some calls. 11 | 12 | 13 | ## Before and since 14 | 15 | You may append a query parameter of `since_id={ID}`, which will then only return objects higher than that ID. 16 | 17 | A query parameter of `before_id={ID}` dictates that the objects will be lower than that ID. 18 | 19 | They may be used in conjunction. 20 | 21 | ## Count 22 | 23 | By default, up to 20 of an object will be returned. A query parameter of `count={N}` can override that. `count` can be `1` to `200` or `-200` to `-1`. If it is invalid, the API will ignore it. 24 | 25 | Negative `count` can be used on most streams. 26 | 27 | Positive will return the newest. Negative will return the oldest. 28 | 29 | 30 | ## Examples 31 | 32 | In these examples, `50` is the most recent post ID. 33 | 34 | ### Oldest since 35 | 36 | This can be used if you do not want to miss posts since your last request. It will get the oldest 20 posts after `4`. 37 | 38 | ```markdown 39 | https://api.pnut.io/v1/posts/streams/global 40 | ?since_id=4 41 | &count=-20 42 | ``` 43 | Returns posts `24`-`5`. 44 | 45 | ### Newest since 46 | 47 | This will skip some posts, because it is getting the 20 newest posts after `4`. You may want to do this, if you want to skip to the latest without retrieving any you already have. 48 | 49 | ```markdown 50 | https://api.pnut.io/v1/posts/streams/global 51 | ?since_id=4 52 | ``` 53 | Returns posts `50`-`31`. 54 | 55 | ### Newest before 56 | 57 | ```markdown 58 | https://api.pnut.io/v1/posts/streams/global 59 | ?before_id=6 60 | &count=4 61 | ``` 62 | Returns posts `5`, `4`, `3`, `2`. 63 | 64 | ### Newest within a range 65 | 66 | ```markdown 67 | https://api.pnut.io/v1/posts/streams/global 68 | ?since_id=4 69 | &before_id=6 70 | ``` 71 | Returns post `5`. -------------------------------------------------------------------------------- /implementation/rate-limits.md: -------------------------------------------------------------------------------- 1 | # Rate Limits 2 | 3 | Every call to the API sends three return headers informing the rate limit. These limits vary based on whether the call is a `GET` or not and whether the call is authenticated or not. 4 | 5 | Authenticated requests are limited on a per-token basis. Uauthenticated calls (no valid token is used) are limited based on the IP address used. 6 | 7 | If a limit is hit, you must wait __600__ seconds before attempting again. The API will return an object with the seconds remaining in `meta.retry_in`, until it resets. 8 | 9 | Here are the three return headers: 10 | 11 | ## X-RateLimit-Limit 12 | 13 | The number of hits to the attempted authentication and method combination allowed before a reset. These are the associated limits: 14 | 15 | * Unauthenticated `GET` requests: 40 16 | * Authenticated `GET` requests: 5000 17 | * `POST`/`PUT`/`PATCH`/`DELETE`: 20 18 | 19 | 20 | ## X-RateLimit-Remaining 21 | 22 | The total number of hits to the attempted authentication and method combination available before the next reset. 23 | 24 | 25 | ## X-RateLimit-Reset 26 | 27 | Seconds remaining before a reset. 28 | 29 | * Unauthenticated `GET` requests: 60 seconds 30 | * Authenticated `GET` requests: 3600 seconds 31 | * `POST`/`PUT`/`PATCH`/`DELETE`: 60 seconds -------------------------------------------------------------------------------- /implementation/raw.md: -------------------------------------------------------------------------------- 1 | # Raw 2 | 3 | 4 | Raw data is arbitrary data that can be attached to posts, users, channels, messages, files, and polls. Clients can specify a `type` and a value for each item in the list of `raw` data. 5 | 6 | Only specified types are verified by the server. Otherwise, clients will have to double-check the data to be sure clients that wrote them did so as expected. Any client can create any type of raw data, but as a developer, you can define a specification for your own type, and publicize what that is, so that other clients can also interpret it. 7 | 8 | The total contents of the `raw` serialized JSON object can be up to 8192 bytes. Because items may already exist on mutable objects, you must check to make sure you will have space before adding to the existing data. 9 | 10 | This is the format of a `raw` JSON object with a single item: 11 | 12 | ```json 13 | "raw": { 14 | "TYPE_NAME": [ 15 | { 16 | "YOUR_KEYS": "YOUR_DATA" 17 | } 18 | ] 19 | } 20 | ``` 21 | 22 | 23 | ## Core Types 24 | 25 | Any `type` starting with `io.pnut.core` is checked by the server for validity, to an extent. Be sure to match their requirements when creating core channel types or `raw` data types. 26 | 27 | If a submission is not valid, the whole operation will be prevented, and an error message will be returned in the `meta`. 28 | 29 | Community-defined types and core types can be referenced on our [object-metadata GitHub repository](https://github.com/pnut-api/object-metadata). 30 | 31 | 32 | 33 | ## Inclusion 34 | 35 | By default across the network, `raw` is not included on objects, and you must request it by setting query parameters: 36 | 37 | * `include_raw=1` 38 | * `include_user_raw=1` 39 | * `include_channel_raw=1` 40 | * `include_message_raw=1` 41 | * `include_post_raw=1` 42 | * `include_file_raw=1` 43 | * `include_poll_raw=1` 44 | 45 | If any relevant parameter is set to `1`, it will be included for the object and any children. It is preferable to not include something if you do not use it. 46 | 47 | 48 | 49 | ## Mutability & Duplicates 50 | 51 | Raw data attached to __posts__, __messages__, and __polls__ is *immutable*. It must be attached on creation of the object, and cannot be changed afterwards. Because they are immutable, they can have multiple raw items of the same type. 52 | 53 | Channels, files, and users are mutable, and can only have one of a particular type. The `raw` data is still formed the same way, as a list for each type, even though there will only be a single item in the list, if it exists. 54 | 55 | 56 | 57 | ## Deleting Mutable Items 58 | 59 | To delete a mutable item, include the `type` with an empty list, `[]`. 60 | 61 | 62 | 63 | ## Example 64 | 65 | ##### Example Post Creation {.example-code} 66 | 67 | ```bash 68 | curl "https://api.pnut.io/v1/posts?include_post_raw=1" \ 69 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 70 | -H "Content-Type: application/json" \ 71 | -d "{ 72 | \"text\": \"I want everyone to know this is in English.\", 73 | \"raw\": { 74 | \"io.pnut.core.language\": [ 75 | { 76 | \"language\": \"en\" 77 | } 78 | ] 79 | } 80 | }" 81 | -X POST` 82 | ``` 83 | 84 | Returns the new post. 85 | 86 | ```json 87 | { 88 | "meta": { 89 | "code": 201 90 | }, 91 | "data": { 92 | "created_at": "2016-12-11T18:14:12Z", 93 | "id": "2384", 94 | "source": { 95 | "id": "3PFPMSet53RutGINA8e5HWqYg_UCDHad", 96 | "name": "Broadsword", 97 | "url": "http://xyz.s3rv.com" 98 | }, 99 | "user": {"...User Object..."}, 100 | "thread_id": "2384", 101 | "counts": { 102 | "bookmarks": 0, 103 | "reposts": 0, 104 | "replies": 0, 105 | "threads": 0 106 | }, 107 | "content": { 108 | "html": "I want everyone to know this is in English", 109 | "text": "I want everyone to know this is in English", 110 | "entities": { 111 | "links": [], 112 | "mentions": [], 113 | "tags": [] 114 | } 115 | }, 116 | "you_bookmarked": false, 117 | "you_reposted": false, 118 | "raw": { 119 | "io.pnut.core.language": [ 120 | { 121 | "language": "en" 122 | } 123 | ] 124 | } 125 | } 126 | } 127 | ``` -------------------------------------------------------------------------------- /implementation/responses.md: -------------------------------------------------------------------------------- 1 | # Responses 2 | 3 | All API endpoints listed under Resources, whether successful or not, will be returned in the same type of envelope structure. 4 | 5 | *The [authentication endpoints](../authentication/overview) return a slightly different format that follows the OAuth2 specification.* 6 | 7 | 8 | ## Response Envelope 9 | 10 | The top-level response is an object containing two keys. The first key, `data`, corresponds to the actual response item requested. This may either be an object itself or a list of objects. The particular data returned is described in each endpoint's documentation. If the request is unsuccessful (results in an error), no `data` key will be present. 11 | 12 | The second key present, `meta`, corresponds to an object containing additional information about the request. This object will always contain `code`, a copy of the HTTP status code that has been returned. It will also contain [pagination metadata](pagination) and/or a [stream marker](../resources/stream-marker) when relevant. 13 | 14 | ##### Sample Response Envelope 15 | ```json 16 | { 17 | "data": "the data you requested", 18 | "meta": { 19 | "more": true, 20 | "max_id": "2703", 21 | "min_id": "2702", 22 | "marker": { 23 | "id": "2593", 24 | "last_read_id": "5719", 25 | "percentage": "0", 26 | "updated_at": "2017-12-26T15:26:19Z", 27 | "version": "YM-fTQk8_0nsdlI01kcUCGvyvHN", 28 | "name": "global" 29 | }, 30 | "code": 200 31 | } 32 | } 33 | ``` 34 | 35 | 36 | ## CORS 37 | 38 | [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) is supported for authenticated cross-domain API requests direct from browsers. Be sure to carefully consider how your app will handle access tokens for CORS requests. 39 | 40 | 41 | 42 | ## X-Pretty-Json Header 43 | 44 | The examples in the documentation include the `X-Pretty-Json` header. This prints the JSON with more readable spacing, for testing. In production there's no reason to include it. If it is set at all, the JSON will be returned that way. 45 | 46 | 47 | 48 | ## Errors 49 | 50 | If the request was unsuccessful, no `data` key will be returned. `code` and `error_message` keys will indicate what error occurred. There may also be a uniquely-identifying `error_id` present that can be helpful when talking to pnut.io support. 51 | 52 | The API will respond with some accuracy for almost all issues. There are two unspecific errors: you may receive `code: 400, error_message: Wrong type`, which should be straight forward to determine the cause from the client. If you receive `code: 500, error_message: Internal Server Error`, something internal failed and it should be immediately reported to support. 53 | 54 | 55 | 56 | ## HTTP status codes 57 | 58 | Pnut.io uses the HTTP status code to indicate if an API request was a success or failure. For environments that can't access the raw HTTP response directly, this value is also duplicated in the `meta.code` value. The following table lists all currently used HTTP status codes. If you get a status code that is not documented below, please [open an issue](https://github.com/pnut-api/api-spec/issues). 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
CodeDescription
200 OKThe request was successful.
204 No ContentReturned when retrieving an incomplete file or when uploading file content to an incomplete file.
302 FoundRedirection to the final destination of a resource. Returned when retrieving user avatar, or a cover image.
400 Bad RequestThe API request was malformed in some way. A message should be returned that indicates how this request can be fixed.
401 UnauthorizedA token is required. If you passed a token, a message should indicate why this token is not authorized.
403 ForbiddenThe token you are providing doesn't have the right to perform the request. Please check that your token is of the correct type and has the required scope.
404 Not FoundThe requested resource was not found.
429 Too Many RequestsThe request could not be completed due to a rate limit. Please wait at the number of seconds specified in the Retry-After header before you retry this request.
500 Internal Server ErrorAn unexpected server error has occurred. Talk to pnut.io support to solve the issue.
507 Insufficient StorageThe request couldn't be completed because the authorized user has hit a quota limit. This could be returned when trying to upload a file. The current token information's data.storage.available key can help an app avoid this error.
110 | -------------------------------------------------------------------------------- /resources/channels/explore.md: -------------------------------------------------------------------------------- 1 | # Explore Streams 2 | 3 | Endpoints: 4 | 5 | * [Get a list of explore streams](#get-channels-streams-explore) 6 | * [Get an explore stream](#get-channels-streams-explore-slug) 7 | 8 | Explore streams are pre-built searches with some simple criteria. 9 | 10 | 11 | ## GET /channels/streams/explore {#get-channels-streams-explore .endpoint} 12 | 13 | Scope: none 14 | 15 | Retrieve a list of explore streams. 16 | 17 | ##### Example {.example-code} 18 | 19 | ```bash 20 | curl "https://api.pnut.io/v1/channels/streams/explore" \ 21 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 22 | -H "X-Pretty-Json: 1" 23 | ``` 24 | 25 | Returns a list of explore streams. 26 | 27 | ```json 28 | { 29 | "meta": { 30 | "code": 200 31 | }, 32 | "data": [ 33 | { 34 | "description": "String", 35 | "slug": "String", 36 | "title": "String", 37 | "url": "https://example.com" 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | 44 | ## GET /channels/streams/explore/{slug} {#get-channels-streams-explore-slug .endpoint} 45 | 46 | Scope: none 47 | 48 | Retrieve a list of channels in an explore stream. 49 | 50 | ### URL Parameters 51 | 52 | Name|Description 53 | -|- 54 | `slug`|Slug of the stream to retrieve channels from. Retrieve slugs to use from the previous call. 55 | 56 | ##### Example {.example-code} 57 | 58 | ```bash 59 | curl "https://api.pnut.io/v1/channels/streams/explore/conversations?count=1" \ 60 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 61 | -H "X-Pretty-Json: 1" 62 | ``` 63 | 64 | Returns a list of channels. 65 | 66 | ```json 67 | { 68 | "meta": { 69 | "more": true, 70 | "max_id": "0", 71 | "min_id": "0", 72 | "code": 200 73 | }, 74 | "data": [ 75 | {"...Channel Object..."} 76 | ] 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /resources/channels/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Channel Lifecycle 2 | 3 | Endpoints: 4 | 5 | * [Create a channel](#post-channels) 6 | * [Update a channel](#put-channels-id) 7 | * [Change channel owner](#put-channels-id-owner) 8 | * [Delete a channel](#delete-channels-id) 9 | 10 | Note that channels will appear "unread" if there is no stream marker for the user+channel, or if the marker's `id` is lower than the most recent message in the channel. 11 | 12 | For details on how to use channels for private messaging, look at [How To Private Message](../../how-to/private-messages). 13 | 14 | 15 | ## POST /channels {#post-channels .endpoint} 16 | 17 | Token: user 18 | 19 | Scope: messages 20 | 21 | Create a channel. 22 | 23 | ### POST Body Data 24 | 25 | Name|Description 26 | -|- 27 | `type`|__Required__ String composed of letters, numbers, underscore, period, and dash. Up to 128 characters 28 | `acl`|Valid ACL object. By default, everything but `type` and `user` is editable after creating the channel! Be sure to restrict editing if you want to prevent it, and read [How to ACL](../../how-to/channel-permissions) 29 | `raw`|Embedded [raw values](/docs/implementation/raw). 30 | 31 | ##### Example {.example-code} 32 | 33 | ```bash 34 | curl "https://api.pnut.io/v1/channels" \ 35 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 36 | -H "Content-Type: application/json" \ 37 | -d "{ 38 | \"type\":\"com.example.site\", 39 | \"acl\":{ 40 | \"full\": { 41 | \"user_ids\": [ 42 | 4,\"@wife\" 43 | ], 44 | \"immutable\":false 45 | }, 46 | \"read\": { 47 | \"any_user\":true 48 | } 49 | } 50 | }" \ 51 | -X POST \ 52 | -H "X-Pretty-Json: 1" 53 | ``` 54 | 55 | Returns the created channel 56 | 57 | ```json 58 | { 59 | "meta": { 60 | "code": 201 61 | }, 62 | "data": {"...Channel Object..."} 63 | } 64 | ``` 65 | 66 | 67 | 68 | ## PUT /channels/{channel_id} {#put-channels-id .endpoint} 69 | 70 | Token: user 71 | 72 | Scope: messages 73 | 74 | Update a channel. `PUT` and `PATCH` may be used. 75 | 76 | ### POST Body Data 77 | 78 | Name|Description 79 | -|- 80 | `acl`|Valid ACL object. Only mutable ACLs can be updated. Full-access users and the channel creator can edit a channel, but only the creator can change who is a full-access user. Be sure to restrict editing if you want to prevent it, and read [How to ACL](../../how-to/channel-permissions) 81 | `raw`|Embedded [raw values](/docs/implementation/raw). Not deleted unless explicitly set to empty values for each type. 82 | 83 | ##### Example {.example-code} 84 | 85 | ```bash 86 | curl "https://api.pnut.io/v1/channels/13" \ 87 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 88 | -H "Content-Type: application/json" \ 89 | -d "{ 90 | \"acl\":{ 91 | \"full\": { 92 | \"user_ids\": [ 93 | 4 94 | ] 95 | } 96 | } 97 | }" \ 98 | -X PUT \ 99 | -H "X-Pretty-Json: 1" 100 | ``` 101 | 102 | Returns the updated channel 103 | 104 | ```json 105 | { 106 | "meta": { 107 | "code": 200 108 | }, 109 | "data": {"...Channel Object..."} 110 | } 111 | ``` 112 | 113 | 114 | 115 | ## PUT /channels/{channel_id}/owner {#put-channels-id-owner .endpoint} 116 | 117 | Token: user 118 | 119 | Scope: messages 120 | 121 | Change the owner of a channel. 122 | 123 | Can not be done to `io.pnut.core.pm`-type channels. 124 | 125 | Can only be done by the current owner of the channel. The current owner will automatically be made a `full` access user of the channel. 126 | 127 | ### POST Body Data 128 | 129 | Name|Description 130 | -|- 131 | `owner_id`|User ID or `@`-username of new owner. 132 | 133 | ##### Example {.example-code} 134 | 135 | ```bash 136 | curl "https://api.pnut.io/v1/channels/13/owner" \ 137 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 138 | -H "Content-Type: application/json" \ 139 | -d "{ 140 | \"owner_id\": \"2\" 141 | }" \ 142 | -X PUT \ 143 | -H "X-Pretty-Json: 1" 144 | ``` 145 | 146 | Returns the updated channel 147 | 148 | ```json 149 | { 150 | "meta": { 151 | "code": 200 152 | }, 153 | "data": {"...Channel Object..."} 154 | } 155 | ``` 156 | 157 | 158 | 159 | 160 | ## DELETE /channels/{channel_id} {#delete-channels-id .endpoint} 161 | 162 | Token: user 163 | 164 | Scope: messages 165 | 166 | Deactivate a channel. Only the creator of a channel can deactivate it. Deactivating a channel removes all subscriptions to the channel, but does nothing to the contents. It is irreversible. `io.pnut.core.pm`-type channels cannot be deactivated. 167 | 168 | ##### Example {.example-code} 169 | 170 | ```bash 171 | curl "https://api.pnut.io/v1/channels/13" \ 172 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 173 | -X DELETE \ 174 | -H "X-Pretty-Json: 1" 175 | ``` 176 | 177 | Returns the deactivated channel 178 | 179 | ```json 180 | { 181 | "meta": { 182 | "code": 200 183 | }, 184 | "data": {"....Channel Object..."} 185 | } 186 | ``` 187 | -------------------------------------------------------------------------------- /resources/channels/muting.md: -------------------------------------------------------------------------------- 1 | # Channel Muting 2 | 3 | Endpoints: 4 | 5 | * [Get the authenticated user's muted channels](#get-users-me-channels-muted) 6 | * [Mute a channel](#put-channels-id-mute) 7 | * [Unmute a channel](#delete-channels-id-mute) 8 | 9 | Muting a channel prevents other users from being able to auto-subscribe you to that channel. 10 | 11 | 12 | ## GET /users/me/channels/muted {#get-users-me-channels-muted .endpoint} 13 | 14 | Token: user 15 | 16 | Scope: messages 17 | 18 | Retrieve a list of channels the authenticated user has muted. 19 | 20 | ##### Example {.example-code} 21 | 22 | ```bash 23 | curl "https://api.pnut.io/v1/users/me/channels/muted" \ 24 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 25 | -H "X-Pretty-Json: 1" 26 | ``` 27 | 28 | Returns a list of muted channels. 29 | 30 | ```json 31 | { 32 | "meta": { 33 | "more": false, 34 | "min_id": "0", 35 | "max_id": "0", 36 | "code": 200 37 | }, 38 | "data": [ 39 | {"...Channel Object..."} 40 | ] 41 | } 42 | ``` 43 | 44 | 45 | 46 | ## PUT /channels/{channel_id}/mute {#put-channels-id-mute .endpoint} 47 | 48 | Token: user 49 | 50 | Scope: messages 51 | 52 | Mute subscriptions for a channel. Muting unsubscribes, if you were subscribed. 53 | 54 | ### URL Parameters 55 | 56 | Name|Description 57 | -|- 58 | `channel_id`|ID of the channel to mute. 59 | 60 | 61 | ##### Example {.example-code} 62 | 63 | ```bash 64 | curl "https://api.pnut.io/v1/channels/2/mute" \ 65 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 66 | -X PUT \ 67 | -H "X-Pretty-Json: 1" 68 | ``` 69 | 70 | Returns the muted channel. 71 | 72 | ```json 73 | { 74 | "meta": { 75 | "code": 200 76 | }, 77 | "data": {"...Channel Object..."} 78 | } 79 | ``` 80 | 81 | 82 | 83 | ## DELETE /channels/{channel_id}/mute {#delete-channels-id-mute .endpoint} 84 | 85 | Token: user 86 | 87 | Scope: messages 88 | 89 | Delete a subscription mute for a channel. 90 | 91 | ### URL Parameters 92 | 93 | Name|Description 94 | -|- 95 | `channel_id`|ID of the channel to unmute. 96 | 97 | 98 | ##### Example {.example-code} 99 | 100 | ```bash 101 | curl "https://api.pnut.io/v1/channels/4/mute" \ 102 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 103 | -X DELETE \ 104 | -H "X-Pretty-Json: 1" 105 | ``` 106 | 107 | Returns the unmuted channel. 108 | 109 | ```json 110 | { 111 | "meta": { 112 | "code": 200 113 | }, 114 | "data": {"....Channel Object..."} 115 | } 116 | ``` 117 | -------------------------------------------------------------------------------- /resources/channels/presence.md: -------------------------------------------------------------------------------- 1 | # Per-Channel User Presence 2 | 3 | Endpoints: 4 | 5 | * [Get users present in a channel](#get-channels-id-presence) 6 | * [Get a single user's presence in a channel](#get-users-id-presence-channels-id) 7 | * [Update the authenticated user's presence in a channel](#put-users-me-presence-channels-id) 8 | 9 | A user's presence can be set for a specific channel, separately from its [global User Presence](/docs/resources/users/presence). It functions identically, but there are no helpers for updating status from other endpoints. 10 | 11 | Getting [users subscribed to a channel](/docs/resources/channels/subscribing#get-channels-id-subscribers) with `?include_presence=1` query parameter will include the users' channel presence on their user object instead of their global presence. 12 | 13 | 14 | ## GET /channels/{channel_id}/presence {#get-channels-id-presence .endpoint} 15 | 16 | Token: user 17 | 18 | Scope: messages 19 | 20 | Retrieve all users' presence statuses that are not "offline". 21 | 22 | ##### Example {.example-code} 23 | 24 | ```bash 25 | curl "https://api.pnut.io/v1/channels/582/presence" \ 26 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 27 | -H "X-Pretty-Json: 1" 28 | ``` 29 | 30 | Returns a list of users' presences. 31 | 32 | ```json 33 | { 34 | "meta": { 35 | "code": 200 36 | }, 37 | "data": [] 38 | } 39 | ``` 40 | 41 | 42 | ## GET /users/{user_id}/presence/channels/{channel_id} {#get-users-id-presence-channels-id .endpoint} 43 | 44 | Token: user 45 | 46 | Scope: messages 47 | 48 | Retrieve a user's presence in a channel. 49 | 50 | If the user has never set a presence, `last_seen_at` will not be set. 51 | 52 | ### URL Parameters 53 | 54 | Name|Description 55 | -|- 56 | `user_id`|ID of the user to retrieve 57 | `channel_id`|ID of the channel to check within 58 | 59 | ##### Example {.example-code} 60 | 61 | ```bash 62 | curl "https://api.pnut.io/v1/users/1/presence/channels/582" \ 63 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 64 | -H "X-Pretty-Json: 1" 65 | ``` 66 | 67 | Returns a user's current presence. 68 | 69 | ```json 70 | { 71 | "meta": { 72 | "code": 200 73 | }, 74 | "data": { 75 | "avatar_image": "String", 76 | "id": "0", 77 | "last_seen_at": "ISO 8601 (Optional)", 78 | "name": "String (Optional)", 79 | "presence": "String", 80 | "username": "String" 81 | } 82 | } 83 | ``` 84 | 85 | 86 | ## PUT /users/me/presence/channels/{channel_id} {#put-users-me-presence-channels-id .endpoint} 87 | 88 | Token: user 89 | 90 | Scope: messages 91 | 92 | Update a user's presence in a channel. 93 | 94 | ### PUT Parameters 95 | 96 | Name|Description 97 | -|- 98 | `presence`|A string up to 100 unicode characters. If not set, or if it is set to `1`, it will be updated to `"online"`. A value of `"offline"` or `0` will delete the user's presence and remove them from the [list of users online](#get-channels-id-presence). 99 | `expiration`|Unix timestamp for when the given presence should expire. Default is 15 minutes into the future. If set too far into the future, will set to the maximum of 7 days. If set to a past timestamp, will delete the user's presence. 100 | 101 | ##### Example {.example-code} 102 | 103 | ```bash 104 | curl "https://api.pnut.io/v1/users/me/presence/channels/582" \ 105 | -X PUT \ 106 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 107 | -d "presence=keeping my stick on the ice&expiration=1721702296" \ 108 | -H "X-Pretty-Json: 1" 109 | ``` 110 | 111 | Returns the updated user presence. 112 | 113 | ```json 114 | { 115 | "meta": { 116 | "code": 200 117 | }, 118 | "data": { 119 | "avatar_image": "String", 120 | "id": "0", 121 | "last_seen_at": "ISO 8601 (Optional)", 122 | "name": "String (Optional)", 123 | "presence": "String", 124 | "username": "String" 125 | } 126 | } 127 | ``` 128 | -------------------------------------------------------------------------------- /resources/channels/search.md: -------------------------------------------------------------------------------- 1 | # Channel Search 2 | 3 | Endpoints: 4 | 5 | * [Search channels](#get-channels-search) 6 | 7 | 8 | ## GET /channels/search {#get-channels-search .endpoint} 9 | 10 | Scope: none 11 | 12 | Retrieve a list of channels filtered by the given criteria. 13 | 14 | ### Query Parameters 15 | 16 | #### Search 17 | 18 | Name|Description 19 | -|- 20 | `q`|Basic text string searched for in `name`s and `description`s in a channel's `io.pnut.core.chat-settings` raw. 21 | 22 | #### Sort 23 | 24 | Name|Description 25 | -|- 26 | `order`|One of activity (most recent message), id (most recently created), or popularity (how many messages have been made). Default is by activity. 27 | 28 | #### Filter 29 | 30 | Name|Description 31 | -|- 32 | `categories`|Comma-separated list of: fun, lifestyle, profession, language, community, tech, event, general. Taken from `io.pnut.core.chat-settings` raw 33 | `channel_types`|Comma-separated list of channel types to include 34 | `created_after`|ISO 8601-formatted timestamp after which channels were created 35 | `created_before`|ISO 8601-formatted timestamp before which channels were created 36 | `owner_id`|Channels owned by the included user ID 37 | `exclude_channel_types`|Comma-separated list of channel types to exclude 38 | `file_id`|Matches with this file attached 39 | `is_public`|`1` or `0` to only include public- or private-readable channels 40 | `raw_types`|Comma-separated list of attached raw types. Any matches returned 41 | 42 | ##### Example {.example-code} 43 | 44 | ```bash 45 | curl "https://api.pnut.io/v1/channels/search?is_public=1&channel_types=io.pnut.core.chat&categories=fun" \ 46 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 47 | -H "X-Pretty-Json: 1" 48 | ``` 49 | 50 | Returns a list of channels 51 | 52 | ```json 53 | { 54 | "meta": { 55 | "more": false, 56 | "max_id": "0", 57 | "min_id": "0", 58 | "code": 200 59 | }, 60 | "data": [ 61 | {"...Channel Object..."} 62 | ] 63 | } 64 | ``` -------------------------------------------------------------------------------- /resources/channels/subscribing.md: -------------------------------------------------------------------------------- 1 | # Channel Subscribing 2 | 3 | Endpoints: 4 | 5 | * [Get the authenticated user's subscribed channels](#get-users-me-channels-subscribed) 6 | * [Get users subscribed to a channel](#get-channels-id-subscribers) 7 | * [Subscribe to a channel](#put-channels-id-subscribe) 8 | * [Unsubscribe from a channel](#delete-channels-id-subscribe) 9 | 10 | Subscribed channels act like an "inbox" of channels ordered by their most recent messages. 11 | 12 | 13 | ## GET /users/me/channels/subscribed {#get-users-me-channels-subscribed .endpoint} 14 | 15 | Token: user 16 | 17 | Scope: messages 18 | 19 | Retrieve a list of channels the authenticated user is subscribed to. 20 | 21 | ##### Example {.example-code} 22 | 23 | ```bash 24 | curl "https://api.pnut.io/v1/users/me/channels/subscribed" \ 25 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 26 | -H "X-Pretty-Json: 1" 27 | ``` 28 | 29 | Returns a list of subscribed channels. 30 | 31 | ```json 32 | { 33 | "meta": { 34 | "more": false, 35 | "max_id": "0", 36 | "min_id": "0", 37 | "unread_counts": { 38 | "io.pnut.core.chat": 0, 39 | "io.pnut.core.pm": 0 40 | }, 41 | "code": 200 42 | }, 43 | "data": [ 44 | {"...Channel Object..."} 45 | ] 46 | } 47 | ``` 48 | 49 | 50 | 51 | ## GET /channels/{channel_id}/subscribers {#get-channels-id-subscribers .endpoint} 52 | 53 | Scope: messages 54 | 55 | Retrieve a list of users subscribed to a channel. 56 | 57 | If `?include_presence=1` is included on the call, the users' `presence` field will reflect their presence in the channel, *not* their global user presence. 58 | 59 | ### URL Parameters 60 | 61 | Name|Description 62 | -|- 63 | `channel_id`|ID of the channel retrieve subscribers for. 64 | 65 | 66 | ##### Example {.example-code} 67 | 68 | ```bash 69 | curl "https://api.pnut.io/v1/channels/18/subscribers" \ 70 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 71 | -H "X-Pretty-Json: 1" 72 | ``` 73 | 74 | Returns a list of users. 75 | 76 | ```json 77 | { 78 | "meta": { 79 | "code": 200 80 | }, 81 | "data": [ 82 | {"...User Object..."} 83 | ] 84 | } 85 | ``` 86 | 87 | 88 | 89 | ## PUT /channels/{channel_id}/subscribe {#put-channels-id-subscribe .endpoint} 90 | 91 | Token: user 92 | 93 | Scope: messages 94 | 95 | Subscribe to updates from a channel. Subscribing unmutes it, if you were muting it. 96 | 97 | ### URL Parameters 98 | 99 | Name|Description 100 | -|- 101 | `channel_id`|ID of the channel to subscribe to. 102 | 103 | 104 | ##### Example {.example-code} 105 | 106 | ```bash 107 | curl "https://api.pnut.io/v1/channels/18/subscribe" \ 108 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 109 | -X PUT \ 110 | -H "X-Pretty-Json: 1" 111 | ``` 112 | 113 | Returns the subscribed channel. 114 | 115 | ```json 116 | { 117 | "meta": { 118 | "code": 200 119 | }, 120 | "data": {"...Channel Object..."} 121 | } 122 | ``` 123 | 124 | 125 | 126 | ## DELETE /channels/{channel_id}/subscribe {#delete-channels-id-subscribe .endpoint} 127 | 128 | Token: user 129 | 130 | Scope: messages 131 | 132 | Delete a subscription for a channel. Unsubscribing also deletes any existing stream marker for the channel. 133 | 134 | ### URL Parameters 135 | 136 | Name|Description 137 | -|- 138 | `channel_id`|ID of the channel to unsubscribe from. 139 | 140 | 141 | ##### Example {.example-code} 142 | 143 | ```bash 144 | curl "https://api.pnut.io/v1/channels/18/subscribe" \ 145 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 146 | -X DELETE \ 147 | -H "X-Pretty-Json: 1" 148 | ``` 149 | 150 | Returns the unsubscribed channel. 151 | 152 | ```json 153 | { 154 | "meta": { 155 | "code": 200 156 | }, 157 | "data": {"....Channel Object..."} 158 | } 159 | ``` 160 | -------------------------------------------------------------------------------- /resources/clients.md: -------------------------------------------------------------------------------- 1 | # Clients 2 | 3 | Endpoints: 4 | 5 | * [Get a user's clients](#get-users-id-clients) 6 | * [Get a client](#get-clients-id) 7 | 8 | Client name can contain any Unicode characters. *Be sure to escape it if necessary.* 9 | 10 | 11 | ## GET /users/{user_id}/clients {#get-users-id-clients .endpoint} 12 | 13 | Scope: any 14 | 15 | Retrieve a list of active clients created by a user. 16 | 17 | ### URL Parameters 18 | 19 | Name|Description 20 | -|- 21 | `user_id`|ID of the user to list clients from 22 | 23 | ##### Example Call {.example-code} 24 | 25 | ```bash 26 | curl "https://api.pnut.io/v1/users/8/clients" \ 27 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 28 | -H "X-Pretty-Json: 1" 29 | ``` 30 | 31 | Returns a list of clients 32 | 33 | ```json 34 | { 35 | "meta": { 36 | "code": 200 37 | }, 38 | "data": [ 39 | { 40 | "id": "String", 41 | "name": "String", 42 | "url": "https://example.com" 43 | } 44 | ] 45 | } 46 | ``` 47 | 48 | 49 | ## GET /clients/{client_id} {#get-clients-id .endpoint} 50 | 51 | Scope: any 52 | 53 | Retrieve details on a public client, by client ID. 54 | 55 | ### URL Parameters 56 | 57 | Name|Description 58 | -|- 59 | `client_id`|ID of the client to retrieve 60 | 61 | ##### Example {.example-code} 62 | 63 | ```bash 64 | curl "https://api.pnut.io/v1/clients/3PFPMSet53RutGINA8e5HWqYg_UCDHad" \ 65 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 66 | -H "X-Pretty-Json: 1" 67 | ``` 68 | 69 | Returns the detailed client object 70 | 71 | ```json 72 | { 73 | "meta": { 74 | "code": 200 75 | }, 76 | "data": { 77 | "created_at": "ISO-8601", 78 | "user": {"...User Object..."}, 79 | "id": "String", 80 | "url": "https://example.com", 81 | "logo_image": "https://example.com/logo.png", 82 | "name": "String", 83 | "posts": 0, 84 | "content": { 85 | "text": "String", 86 | "html": "String", 87 | "entities": { 88 | "links": [], 89 | "mentions": [], 90 | "tags": [] 91 | } 92 | } 93 | } 94 | } 95 | ``` 96 | -------------------------------------------------------------------------------- /resources/files/lookup.md: -------------------------------------------------------------------------------- 1 | # File Lookup 2 | 3 | Endpoints: 4 | 5 | * [Get a file](#get-files-id) 6 | * [Get multiple files](#get-files) 7 | * [Get the authenticated user's files](#get-users-me-files) 8 | 9 | 10 | ## GET /files/{file_id} {#get-files-id .endpoint} 11 | 12 | Token: user 13 | 14 | Scope: files 15 | 16 | Retrieve a file object. If `url_expires_at` has expired, this will update the `url` and any embedded references to it. 17 | 18 | ### URL Parameters 19 | 20 | Name|Description 21 | -|- 22 | `file_id`|ID of the requested file 23 | 24 | ##### Example {.example-code} 25 | 26 | ```bash 27 | curl "https://api.pnut.io/v1/files/69" \ 28 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 29 | -H "X-Pretty-Json: 1" 30 | ``` 31 | 32 | Returns the requested file details 33 | 34 | ```json 35 | { 36 | "meta": { 37 | "code": 200 38 | }, 39 | "data": {"...File Object..."} 40 | } 41 | ``` 42 | 43 | 44 | ## GET /files {#get-files .endpoint} 45 | 46 | Token: user 47 | 48 | Scope: files 49 | 50 | Retrieve a list of specified file objects. Only returns the first 200 found. 51 | 52 | If the file needs a `file_token_read` to access, you may include them with query parameters in the pattern `?file_token_read_{file_id}=xxx&file_token_read_{file_id}=xxx`. 53 | 54 | ### Query String Parameters 55 | 56 | Name|Description 57 | -|- 58 | `ids`|Comma-separated list of file IDs 59 | 60 | ##### Example {.example-code} 61 | 62 | ```bash 63 | curl "https://api.pnut.io/v1/files?ids=69,71" \ 64 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 65 | -H "X-Pretty-Json: 1" 66 | ``` 67 | 68 | Returns a list of files 69 | 70 | ```json 71 | { 72 | "meta": { 73 | "code": 200 74 | }, 75 | "data": [ 76 | {"...File Object..."} 77 | ] 78 | } 79 | ``` 80 | 81 | 82 | ## GET /users/me/files {#get-users-me-files .endpoint} 83 | 84 | Token: user 85 | 86 | Scope: files 87 | 88 | Retrieve a list of files created by the authenticated user. 89 | 90 | ##### Example {.example-code} 91 | 92 | ```bash 93 | curl "https://api.pnut.io/v1/users/me/files" \ 94 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 95 | -H "X-Pretty-Json: 1" 96 | ``` 97 | 98 | Returns a list of files 99 | 100 | ```json 101 | { 102 | "meta": { 103 | "code": 200, 104 | "more": false, 105 | "max_id": "0", 106 | "min_id": "0" 107 | }, 108 | "data": [ 109 | {"...File Object..."} 110 | ] 111 | } 112 | ``` -------------------------------------------------------------------------------- /resources/messages/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Message Lifecycle 2 | 3 | Endpoints: 4 | 5 | * [Create a message](#post-channels-id-messages) 6 | * [Delete a message](#delete-channels-id-messages-id) 7 | 8 | 9 | ## POST /channels/{channel_id}/messages {#post-channels-id-messages .endpoint} 10 | 11 | Token: user 12 | 13 | Scope: messages 14 | 15 | Create a message in a channel. 16 | 17 | On creation, you can automatically update the stream marker to the most recent ID in the channel (marking the channel "read") by including `update_marker=1` in the query string. 18 | 19 | For details on how to use channels for private messaging, look at [How To Private Message](../../how-to/private-messages). 20 | 21 | Must be `application/json` Content-Type. 22 | 23 | ### URL Parameters 24 | 25 | Name|Description 26 | -|- 27 | `channel_id`|ID of the channel to create a message in. 28 | 29 | ### POST Body Data 30 | 31 | Name|Description 32 | -|- 33 | `text`|__Required__ 2048 character-limited string 34 | `reply_to`|ID of another message to reply to 35 | `is_nsfw`|Boolean whether the message should be marked as "NSFW" (Not Safe For Work/mature/offensive). Including the tag `#nsfw` in the message body will mark a message NSFW unless overridden by this. 36 | `entities.parse_links`|Boolean whether the links should be parsed by the server. Default `true` 37 | `entities.parse_markdown_links`|Boolean whether the markdown links should be parsed by the server. Default `true` 38 | `raw`|Embedded [raw values](/docs/implementation/raw). 39 | 40 | ##### Example {.example-code} 41 | 42 | ```bash 43 | curl "https://api.pnut.io/v1/channels/5/messages" \ 44 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 45 | -H "Content-Type: application/json" \ 46 | -d "{\"text\": \"This is a message!\"}" \ 47 | -X POST \ 48 | -H "X-Pretty-Json: 1" 49 | ``` 50 | 51 | Returns the message created. 52 | 53 | ```json 54 | { 55 | "meta": { 56 | "code": 201 57 | }, 58 | "data": {"...Message Object..."} 59 | } 60 | ``` 61 | 62 | 63 | 64 | ## DELETE /channels/{channel_id}/messages/{message_id} {#delete-channels-id-messages-id .endpoint} 65 | 66 | Token: user 67 | 68 | Scope: messages 69 | 70 | Delete a message in a channel. Creators of messages can delete their messages even if they no longer have access to the channel. 71 | 72 | Channel creators and full-access users may also delete others' messages in non-private message channels, which will also create a `deleted_by` field on those deleted messages. 73 | 74 | ### URL Parameters 75 | 76 | Name|Description 77 | -|- 78 | `channel_id`|ID of the channel to delete a message from. 79 | `message_id`|ID of the message to delete. 80 | 81 | 82 | ##### Example {.example-code} 83 | 84 | ```bash 85 | curl "https://api.pnut.io/v1/channels/5/messages/12" \ 86 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 87 | -X DELETE \ 88 | -H "X-Pretty-Json: 1" 89 | ``` 90 | 91 | Returns the deleted message. 92 | 93 | ```json 94 | { 95 | "meta": { 96 | "code": 200 97 | }, 98 | "data": {"...Message Object..."} 99 | } 100 | ``` -------------------------------------------------------------------------------- /resources/messages/lookup.md: -------------------------------------------------------------------------------- 1 | # Messages Lookup 2 | 3 | Endpoints: 4 | 5 | * [Get a message](#get-channels-id-messages-id) 6 | * [Get messages in a thread](#get-channels-id-messages-id-thread) 7 | * [Get multiple messages](#get-messages) 8 | * [Get messages in a channel](#get-channels-id-messages) 9 | * [Get messages by a user](#get-users-me-messages) 10 | 11 | 12 | ## GET /channels/{channel_id}/messages/{message_id} {#get-channels-id-messages-id .endpoint} 13 | 14 | Scope: messages 15 | 16 | Retrieve a message from a channel. The requesting user must have access to the channel or have created the message. 17 | 18 | ### URL Parameters 19 | 20 | Name|Description 21 | -|- 22 | `channel_id`|ID of the channel to retrieve a message from. 23 | `message_id`|ID of the message to retrieve. 24 | 25 | 26 | ##### Example {.example-code} 27 | 28 | ```bash 29 | curl "https://api.pnut.io/v1/channels/5/messages/11" \ 30 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 31 | -H "X-Pretty-Json: 1" 32 | ``` 33 | 34 | Returns the requested message. 35 | 36 | ```json 37 | { 38 | "meta": { 39 | "code": 200 40 | }, 41 | "data": {"...Message Object..."} 42 | } 43 | ``` 44 | 45 | 46 | 47 | ## GET /channels/{channel_id}/messages/{message_id}/thread {#get-channels-id-messages-id-thread .endpoint} 48 | 49 | Token: user 50 | 51 | Scope: messages 52 | 53 | Retrieve messages in the same thread of a channel. All messages will have the same `thread_id` (they are all replies to the same message, or is not a reply to any message). The requesting user must have access to the channel. 54 | 55 | ### URL Parameters 56 | 57 | Name|Description 58 | -|- 59 | `channel_id`|ID of the channel to retrieve messages from. 60 | `message_id`|ID of the message whose thread to retrieve. 61 | 62 | 63 | ##### Example {.example-code} 64 | 65 | ```bash 66 | curl "https://api.pnut.io/v1/channels/5/messages/13/thread" \ 67 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 68 | -H "X-Pretty-Json: 1" 69 | ``` 70 | 71 | Returns a list of messages. 72 | 73 | ```json 74 | { 75 | "meta": { 76 | "more": false, 77 | "max_id": "0", 78 | "min_id": "0", 79 | "code": 200 80 | }, 81 | "data": [ 82 | {"...Message Object..."} 83 | ] 84 | } 85 | ``` 86 | 87 | 88 | 89 | ## GET /channels/messages {#get-messages .endpoint} 90 | 91 | Scope: messages 92 | 93 | Retrieve a list of specified messages. Will only return the first 200 found. 94 | 95 | ### Query String Parameters 96 | 97 | Name|Description 98 | -|- 99 | `ids`|Comma-separated list of message IDs. 100 | 101 | ##### Example {.example-code} 102 | 103 | ```bash 104 | curl "https://api.pnut.io/v1/channels/messages?ids=4,11,12" \ 105 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 106 | -H "X-Pretty-Json: 1" 107 | ``` 108 | 109 | Returns a list of the messages found. 110 | 111 | ```json 112 | { 113 | "meta": { 114 | "code": 200 115 | }, 116 | "data": [ 117 | {"...Message Object..."}, 118 | {"...Message Object..."}, 119 | {"...Message Object..."} 120 | ] 121 | } 122 | ``` 123 | 124 | 125 | ## GET /channels/{channel_id}/messages {#get-channels-id-messages .endpoint} 126 | 127 | Scope: messages 128 | 129 | Retrieve paginated messages from a channel. 130 | 131 | ### URL Parameters 132 | 133 | Name|Description 134 | -|- 135 | `channel_id`|ID of the channel to retrieve messages from 136 | 137 | ##### Example {.example-code} 138 | 139 | ```bash 140 | curl "https://api.pnut.io/v1/channels/2/messages" \ 141 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 142 | -H "X-Pretty-Json: 1" 143 | ``` 144 | 145 | Returns a list of messages from the channel. 146 | 147 | ```json 148 | { 149 | "meta": { 150 | "more": false, 151 | "max_id": "0", 152 | "min_id": "0", 153 | "marker": { 154 | "name": "channel:0" 155 | }, 156 | "code": 200 157 | }, 158 | "data": [ 159 | {"...Message Object..."} 160 | ] 161 | } 162 | ``` 163 | 164 | 165 | ## GET /users/me/messages {#get-users-me-messages .endpoint} 166 | 167 | Scope: messages 168 | 169 | Retrieve a paginated list of messages created by the authenticated user. 170 | 171 | ##### Example {.example-code} 172 | 173 | ```bash 174 | curl "https://api.pnut.io/v1/users/me/messages" \ 175 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 176 | -H "X-Pretty-Json: 1" 177 | ``` 178 | 179 | Returns a list of messages. 180 | 181 | ```json 182 | { 183 | "meta": { 184 | "more": true, 185 | "max_id": "0", 186 | "min_id": "0", 187 | "code": 200 188 | }, 189 | "data": [ 190 | {"...Message Object..."} 191 | ] 192 | } 193 | ``` -------------------------------------------------------------------------------- /resources/messages/report.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | Endpoints: 4 | 5 | * [Report a message](#post-channels-id-messages-id-report) 6 | 7 | The current reasons that will be honored for reporting are: 8 | 9 | * `account_type`: posting in a behavior counter to the purposes of [account types](https://pnut.io/about/account-types) 10 | * `nsfw`: unflagged mature material according to [the community guidelines](https://pnut.io/about/mature-content) 11 | * `soliciting`: unwelcome soliciting 12 | * `user_abuse`: use of the API or network to abuse another user 13 | 14 | 15 | ## POST /channels/{channel_id}/messages/{message_id}/report {#post-channels-id-messages-id-report .endpoint} 16 | 17 | Token: user 18 | 19 | Scope: any 20 | 21 | Report a message in a channel for abuse. 22 | 23 | To test this endpoint, report a message by user [@testuser](/docs/testuser). 24 | 25 | ### URL Parameters 26 | 27 | Name|Description 28 | -|- 29 | `channel_id`|ID of the channel. 30 | `message_id`|ID of the message to report. 31 | 32 | ### POST Body Data 33 | 34 | Name|Description 35 | -|- 36 | `reason`|One of: `account_type`, `nsfw`, `soliciting`, `user_abuse`. 37 | 38 | ##### Example {.example-code} 39 | 40 | ```bash 41 | curl "https://api.pnut.io/v1/channels/18/messages/0/report" \ 42 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 43 | -H "X-Pretty-Json: 1" 44 | ``` 45 | 46 | Returns a 201 47 | 48 | ```json 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /resources/messages/search.md: -------------------------------------------------------------------------------- 1 | # Message Search 2 | 3 | Endpoints: 4 | 5 | * [Search messages](#get-channels-messages-search) 6 | 7 | 8 | ## GET /channels/messages/search {#get-channels-messages-search .endpoint} 9 | 10 | Scope: none 11 | 12 | Retrieve a list of messages filtered by the given criteria. 13 | 14 | ### Query Parameters 15 | 16 | #### Search 17 | 18 | Name|Description 19 | -|- 20 | `q`|List of words included in messages 21 | 22 | #### Sort 23 | 24 | Name|Description 25 | -|- 26 | `order`|One of id or relevance. Default is by relevance 27 | 28 | #### Filter 29 | 30 | Name|Description 31 | -|- 32 | `channel_ids`|__Required__ `pm` to search all accessible private messages, or comma-separated list of channel IDs 33 | `client_id`|Only include messages created by this client ID 34 | `created_after`|ISO 8601-formatted timestamp after which messages were created 35 | `created_before`|ISO 8601-formatted timestamp before which messages were created 36 | `creator_id`|Only include messages created by this user ID 37 | `file_id`|Matches with this file attached 38 | `file_kinds`|Comma-separated list of oEmbed-attached file types (`video`, `audio`, `image`, `other`) 39 | `is_nsfw`|If `0`, does not include NSFW messages 40 | `is_reply`|`1` or `0` to only include messages that are or are not replies 41 | `is_sticky`|If `1`, only include sticky messages 42 | `leading_mentions`|Comma-separated list of mentions at the beginning of a message. Any matches returned 43 | `mentions`|Comma-separated list of mentions. Any matches returned 44 | `poll_id`|Matches with this poll attached 45 | `raw_types`|Comma-separated list of attached raw types. Any matches returned 46 | `reply_to`|Only include messages replying to this message 47 | `tags`|Comma-separated list of tags. Any matches returned 48 | `thread_id`|Only include messages in this thread 49 | `url_domains`|Comma-separated list of domains. Any matches returned. Do not include `http://` or `www` in front of domain 50 | `urls`|Comma-separated list of URLs. Any matches returned 51 | `user_types`|Comma-separated list of user types of: human, feed, bot 52 | 53 | ##### Example {.example-code} 54 | 55 | ```bash 56 | curl "https://api.pnut.io/v1/channels/messages/search?channel_ids=600,18" \ 57 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 58 | -H "X-Pretty-Json: 1" 59 | ``` 60 | 61 | Returns a list of messages 62 | 63 | ```json 64 | { 65 | "meta": { 66 | "more": false, 67 | "max_id": "0", 68 | "min_id": "0", 69 | "code": 200 70 | }, 71 | "data": [ 72 | {"...Message Object..."} 73 | ] 74 | } 75 | ``` -------------------------------------------------------------------------------- /resources/messages/sticky.md: -------------------------------------------------------------------------------- 1 | # Sticky Messages 2 | 3 | Endpoints: 4 | 5 | * [Get sticky messages in a channel](#get-channels-id-sticky_messages) 6 | * [Sticky a message](#put-channels-id-messages-id-sticky) 7 | * [Unsticky a message](#delete-channels-id-messages-id-sticky) 8 | 9 | "Sticky" messages are like bookmarks for a channel, but they are per-channel, not per-user. They will have `is_sticky: true`. The channel also has an indicator, `channel.has_sticky_messages` that will tell if the channel has sticky messages. 10 | 11 | Users with `full` access to a channel are able to sticky and un-sticky messages in the channel. 12 | 13 | In Private Message channels (type `io.pnut.core.pm`), anyone can stick and un-sticky messages. 14 | 15 | 16 | ## GET /channels/{channel_id}/sticky_messages {#get-channels-id-sticky_messages .endpoint} 17 | 18 | Token: user 19 | 20 | Scope: messages 21 | 22 | Retrieve sticky messsages in a channel. The requesting user must have access to the channel. 23 | 24 | Note that the `pagination_id` and `min_id` and `max_id` will change as users sticky and un-sticky messages. 25 | 26 | ### URL Parameters 27 | 28 | Name|Description 29 | -|- 30 | `channel_id`|ID of the channel to retrieve messages from 31 | 32 | 33 | ##### Example {.example-code} 34 | 35 | ```bash 36 | curl "https://api.pnut.io/v1/channels/5/sticky_messages" \ 37 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 38 | -H "X-Pretty-Json: 1" 39 | ``` 40 | 41 | Returns a list of messages. 42 | 43 | ```json 44 | { 45 | "meta": { 46 | "more": false, 47 | "max_id": "0", 48 | "min_id": "0", 49 | "code": 200 50 | }, 51 | "data": [ 52 | {"...Message Object..."} 53 | ] 54 | } 55 | ``` 56 | 57 | 58 | ## PUT /channels/{channel_id}/messages/{message_id}/sticky {#put-channels-id-messages-id-sticky .endpoint} 59 | 60 | Token: user 61 | 62 | Scope: messages 63 | 64 | Sticky a message. 65 | 66 | ### URL Parameters 67 | 68 | Name|Description 69 | -|- 70 | `channel_id`|ID of the channel the message is in 71 | `message_id`|Message to sticky 72 | 73 | 74 | ##### Example {.example-code} 75 | 76 | ```bash 77 | curl "https://api.pnut.io/v1/channels/5/messages/13/sticky" \ 78 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 79 | -X PUT \ 80 | -H "X-Pretty-Json: 1" 81 | ``` 82 | 83 | Returns the stickied message. 84 | 85 | ```json 86 | { 87 | "meta": { 88 | "code": 200 89 | }, 90 | "data": {"...Message Object..."} 91 | } 92 | ``` 93 | 94 | 95 | ## DELETE /channels/{channel_id}/messages/{message_id}/sticky {#delete-channels-id-messages-id-sticky .endpoint} 96 | 97 | Token: user 98 | 99 | Scope: messages 100 | 101 | Un-sticky a message. 102 | 103 | ### URL Parameters 104 | 105 | Name|Description 106 | -|- 107 | `channel_id`|ID of the channel the message is in 108 | `message_id`|Message to un-sticky 109 | 110 | ##### Example {.example-code} 111 | 112 | ```bash 113 | curl "https://api.pnut.io/v1/channels/5/messages/13/sticky" \ 114 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 115 | -X DELETE \ 116 | -H "X-Pretty-Json: 1" 117 | ``` 118 | 119 | Returns the un-stickied message. 120 | 121 | ```json 122 | { 123 | "meta": { 124 | "code": 200 125 | }, 126 | "data": {"....Message Object..."} 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /resources/other/index.md: -------------------------------------------------------------------------------- 1 | # Other 2 | 3 | Miscellany. -------------------------------------------------------------------------------- /resources/other/oembed.md: -------------------------------------------------------------------------------- 1 | # oEmbed 2 | 3 | Pnut has basic oEmbed support. 4 | 5 | Base URL: `https://api.pnut.io/v1/oembed` 6 | 7 | The only format supported is JSON. 8 | 9 | 10 | ## Posts 11 | 12 | To retrieve data for a post, include the query parameter `?url=https://posts.pnut.io/{post_id}`. 13 | 14 | 15 | ## Audio 16 | 17 | To retrieve data for an audio file, include the query parameter `?url=https://audio.pnut.io/{file_id}`. 18 | 19 | 20 | ## Image 21 | 22 | To retrieve data for an image file, include the query parameter `?url=https://photos.pnut.io/{file_id}`. -------------------------------------------------------------------------------- /resources/other/rss.md: -------------------------------------------------------------------------------- 1 | # RSS 2 | 3 | Endpoints: 4 | 5 | * [Tagged posts](#get-feed-rss-posts-tags-tag) 6 | * [User posts](#get-feed-rss-users-id-posts) 7 | * [Post search](#get-feed-rss-posts-search) 8 | * [Channel messages](#get-feed-rss-channels-id-messages) 9 | * [Channel search](#get-feed-rss-channels-search) 10 | 11 | This subset of endpoints can be accessed via RSS. Note that filter query parameters will work. 12 | 13 | * [Template URIs](#template-uris) 14 | 15 | 16 | ## Template URIs {#template-uris} 17 | 18 | URLs in the RSS feeds will by default link to post and message paths on https://beta.pnut.io. However, you may want to expose an RSS feed in your client that links to a different app. This is especially useful for custom channel types, which will not show up properly on the Beta web app. 19 | 20 | To use template URIs in a feed, append `?uri_template=[YOUR URL]` to the feed URL. Available replacements: 21 | 22 | * `{post_id}` 23 | * `{channel_id}` 24 | * `{message_id}` 25 | * `{username}` 26 | * `{user_id}` 27 | 28 | For example, https://pnut.gallery uses a channel for every "gallery" in the app. It then links to a gallery like [https://pnut.gallery/media/{channel_id}/{message_id}](https://api.pnut.io/v1/feed/rss/channels/1552/messages?uri_template=https%3A%2F%2Fpnut.gallery%2Fmedia%2F%7Bchannel_id%7D%2F%7Bmessage_id%7D). 29 | 30 | 31 | 32 | ## GET /feed/rss/posts/tags/{tag} {#get-feed-rss-posts-tags-tag .endpoint} 33 | 34 | The API exposes [tagged posts](../posts/streams#get-posts-tag-tag) as RSS at `https://api.pnut.io/v1/feed/rss/posts/tags/{tag}`. 35 | 36 | 37 | ## GET /feed/rss/users/{user_id}/posts {#get-feed-rss-users-id-posts .endpoint} 38 | 39 | [A user's posts](../posts/streams#get-users-id-posts) are exposed as RSS at `https://api.pnut.io/v1/feed/rss/users/{user_id}/posts`. 40 | 41 | 42 | ## GET /feed/rss/posts/search {#get-feed-rss-posts-search .endpoint} 43 | 44 | [Post search results](../posts/search#get-posts-search) are exposed as RSS at `https://api.pnut.io/v1/feed/rss/posts/search`. 45 | 46 | 47 | ## GET /feed/rss/channels/{channel_id}/messages {#get-feed-rss-channels-id-messages .endpoint} 48 | 49 | [Messages](../messages/lookup#get-channels-id-messages) are exposed as RSS at `https://api.pnut.io/v1/feed/rss/channels/{id}/messages`. 50 | 51 | Note that `?access_token={token}` will have to be appended to non-public channels. 52 | 53 | 54 | ## GET /feed/rss/channels/search {#get-feed-rss-channels-search .endpoint} 55 | 56 | [Channel search results](../channels/search#get-channels-search) are exposed as RSS at `https://api.pnut.io/v1/feed/rss/channels/search`. 57 | -------------------------------------------------------------------------------- /resources/other/system.md: -------------------------------------------------------------------------------- 1 | # System 2 | 3 | Endpoints: 4 | 5 | * [Get configuration](#get-sys-config) 6 | * [Get status of an operation](#get-sys-ops-id) 7 | * [Get statistics](#get-sys-stats) 8 | 9 | The System endpoint gives access to system-wide information. 10 | 11 | 12 | ## GET /sys/config {#get-sys-config .endpoint} 13 | 14 | Scope: none 15 | 16 | Retrieve a list of parameters for interacting with the API. 17 | 18 | ##### Example {.example-code} 19 | 20 | ```bash 21 | curl "https://api.pnut.io/v1/sys/config" \ 22 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 23 | -H "X-Pretty-Json: 1" 24 | ``` 25 | 26 | Returns a catalog of parameters 27 | 28 | ```json 29 | { 30 | "meta": { 31 | "code": 200 32 | }, 33 | "data": { 34 | "file": { 35 | "audio_max_size_bytes": 52428800, 36 | "max_size_bytes": 104857600 37 | }, 38 | "message": { 39 | "max_length": 2048 40 | }, 41 | "post": { 42 | "max_length": 256, 43 | "seconds_between_duplicates": 60, 44 | "seconds_for_revision": 300 45 | }, 46 | "rate_limit": { 47 | "anonymous": { 48 | "reads": 40, 49 | "read_reset_seconds": 60 50 | }, 51 | "authorized": { 52 | "reads": 5000, 53 | "read_reset_seconds": 3600, 54 | "writes": 20, 55 | "write_reset_seconds": 60 56 | }, 57 | "seconds_banned": 600 58 | }, 59 | "raw": { 60 | "max_length": 8192 61 | }, 62 | "user": { 63 | "description_max_length": 256, 64 | "name_max_length": 50, 65 | "presence_max_length": 100, 66 | "username_max_length": 20 67 | } 68 | } 69 | } 70 | ``` 71 | 72 | 73 | 74 | ## GET /sys/ops/{ops_id} {#get-sys-ops-id .endpoint} 75 | 76 | Scope: any 77 | 78 | Retrieve the status of a long-running operation. 79 | 80 | Currently the only API call that creates a long-running operation is [deleting multiple files](/docs/resources/files/lifecycle#delete-files), type `FileDelete`. 81 | 82 | `ended_at` will only be set if the operation has completed. `status` will be one of `running`, `dead`, and `completed`. `dead` is not definitive, but if `dead`, something went wrong. 83 | 84 | ### URL Parameters 85 | 86 | Name|Description 87 | -|- 88 | `ops_id`|UUID of the operation to retrieve details for. 89 | 90 | ### Return Object Parameters 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
FieldTypeDescription
created_atstringTime at which the operation was created, in ISO 8601 format; YYYY-MM-DDTHH:MM:SSZ.
ended_atstringOptional time at which the operation was completed, in ISO 8601 format; YYYY-MM-DDTHH:MM:SSZ.
idstringUUID that identifies the operation.
typestringCurrently only file_delete
statusstringOne of running, dead, completed.
124 | 125 | ##### Example {.example-code} 126 | 127 | ```bash 128 | curl "https://api.pnut.io/v1/sys/ops/5c689c8d-5d59-4974-8705-f20b7abef338" \ 129 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 130 | -H "X-Pretty-Json: 1" 131 | ``` 132 | 133 | Returns the status. 134 | 135 | ```json 136 | { 137 | "data": { 138 | "created_at": "ISO-8601", 139 | "ended_at": "ISO-8601 (Optional)", 140 | "id": "UUID", 141 | "type": "String", 142 | "status": "String" 143 | }, 144 | "meta": { 145 | "code": 200 146 | } 147 | } 148 | ``` 149 | 150 | 151 | 152 | ## GET /sys/stats {#get-sys-stats .endpoint} 153 | 154 | Scope: none 155 | 156 | Retrieve basic statistics for the network. 157 | 158 | ##### Example {.example-code} 159 | 160 | ```bash 161 | curl "https://api.pnut.io/v1/sys/stats" \ 162 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 163 | -H "X-Pretty-Json: 1" 164 | ``` 165 | 166 | Returns a list of statistics 167 | 168 | ```json 169 | { 170 | "meta": { 171 | "code": 200 172 | }, 173 | "data": { 174 | "clients": { 175 | "created": 0, 176 | "public": 0 177 | }, 178 | "days": 0, 179 | "files": { 180 | "created": 0 181 | }, 182 | "messages": { 183 | "created": 0 184 | }, 185 | "polls": { 186 | "created": 0 187 | }, 188 | "posts": { 189 | "created": 0 190 | }, 191 | "users": { 192 | "active": { 193 | "YYYY-MM-DD": 0 194 | }, 195 | "created": 0, 196 | "disabled": 0, 197 | "present": 0 198 | } 199 | } 200 | } 201 | ``` 202 | -------------------------------------------------------------------------------- /resources/other/text-process.md: -------------------------------------------------------------------------------- 1 | # Text Processor 2 | 3 | Endpoints: 4 | 5 | * [Test text processing](#post-text-process) 6 | 7 | Use this endpoint to test rendering of post content. Particularly useful for debugging, or if you want to show a user exactly how the API will process their post. 8 | 9 | 10 | ## POST /text/process {#post-text-process .endpoint} 11 | 12 | Scope: any 13 | 14 | Submit a Content-Type of `application/json` body as if creating a post. Returns an object containing the parsed text with entities. 15 | 16 | ### Query String Parameters 17 | 18 | Name|Description 19 | -|- 20 | `object_type`|`post` or `message`. By default, `text` character length is restricted to 256, like a post. You may change it to rendering like a message with up to 2048 characters by including this parameter with a value of `message`. 21 | 22 | ##### Example {.example-code} 23 | 24 | ```bash 25 | curl "https://api.pnut.io/v1/text/process" \ 26 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 27 | -H "Content-Type: application/json" \ 28 | -d "{\"text\":\"This is a test mentioning @thrrgilag.\"}" \ 29 | -X POST \ 30 | -H "X-Pretty-Json: 1" 31 | ``` 32 | 33 | Returns the parsed text similar to how it would be presented in a post 34 | 35 | ```json 36 | { 37 | "meta": { 38 | "code": 200 39 | }, 40 | "data": { 41 | "text": "...", 42 | "links": [], 43 | "tags": [], 44 | "mentions": [], 45 | "html": "..." 46 | } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /resources/polls/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Poll Lifecycle 2 | 3 | Endpoints: 4 | 5 | * [Create a poll](#post-polls) 6 | * [Respond to a poll](#put-polls-id-response) 7 | * [Delete a poll](#delete-polls-id) 8 | 9 | For an explanation of how to attach polls to other objects, read [How To File](../../how-to/file). Functionally they are almost identical: Create a poll and use the `+io.pnut.core.poll` replacement raw value on a `io.pnut.core.poll-notice` raw item. 10 | 11 | You may also look at the [GitHub object-metadata](https://github.com/pnut-api/object-metadata/blob/master/raw-replacement-values/%2Bio.pnut.core.poll.md). 12 | 13 | 14 | ## POST /polls {#post-polls .endpoint} 15 | 16 | Token: user 17 | 18 | Scope: polls,write_post 19 | 20 | Create a poll. By default, the poll will be private and anonymous. 21 | 22 | Content-Type of `application/json`. 23 | 24 | ### POST Body Data 25 | 26 | Name|Description 27 | -|- 28 | `closed_at`|__Required__ (if `duration` not set) ISO 8601 timestamp at least 1 minute and no more than 2 weeks in the future, after which no one can respond to it 29 | `duration`|__Required__ (if `closed_at` not set) number of minutes the poll should be open, minimum of 1 and maximum of 20160 30 | `options`|__Required__ List of 2 to 10 options must be specified, each with `text` and optional `position` (if you want to specify their order) 31 | `prompt`|__Required__ 256-character explanation or question for the `options` (to be displayed and attached to the object) 32 | `type`|__Required__ Reverse domain name-style identifier of the poll type. E.g., `com.example.site` 33 | `is_anonymous`|If false, user IDs will be identified in the final poll 34 | `is_public`|If true, poll is public 35 | `max_options`|Number of options a user can respond with, from 1 to the total number of `options`. Default is 1 36 | 37 | 38 | ##### Example {.example-code} 39 | 40 | ```bash 41 | curl "https://api.pnut.io/v1/polls" \ 42 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 43 | -H "Content-Type: application/json" \ 44 | -d "{ 45 | \"type\":\"com.example.site\", 46 | \"prompt\":\"Do you like Pnut?\", 47 | \"options\":[ 48 | { 49 | \"text\":\"Yes\" 50 | }, 51 | { 52 | \"text\":\"Of course\" 53 | } 54 | ], 55 | \"duration\":\"60\", 56 | \"is_anonymous\":\"false\" 57 | }" \ 58 | -X POST \ 59 | -H "X-Pretty-Json: 1" 60 | ``` 61 | 62 | Returns the created poll 63 | 64 | ```json 65 | { 66 | "meta": { 67 | "code": 201 68 | }, 69 | "data": {"...Poll Object..."} 70 | } 71 | ``` 72 | 73 | 74 | 75 | ## PUT /polls/{poll_id}/response {#put-polls-id-response .endpoint} 76 | 77 | Token: user 78 | 79 | Scope: polls,write_post 80 | 81 | Respond to a poll. 82 | 83 | Only `human`-type accounts may respond to polls. 84 | 85 | If a poll is private, you must inherently have access to the poll to respond, or include a `poll_token` in the query string. Polls attached to posts, for example, always include `poll_token` in the raw data. Since you might not know if a poll is public or private in such a case, you should always include the poll token when you have it. 86 | 87 | Responses can be changed until the poll closes. 88 | 89 | An empty positions will indicate to clear any responses for the user, but they will still be notified by E-mail when the poll closes, if they have the option enabled on https://pnut.io. 90 | 91 | ### URL Parameters 92 | 93 | Name|Description 94 | -|- 95 | `poll_id`|ID of the poll to respond to 96 | 97 | ### Query Parameters 98 | 99 | Name|Description 100 | -|- 101 | `poll_token`|Required on private polls when you don't know or aren't guaranteed access 102 | `positions`|Instead of including `positions` as POST body data, you can include it as a query parameter, either as array or a comma-separated string of IDs 103 | 104 | ### POST Body Data 105 | 106 | Name|Description 107 | -|- 108 | `positions`|Positions of the options to respond with, if not including `positions` as a query parameter 109 | 110 | ##### Example {.example-code} 111 | 112 | ```bash 113 | curl "https://api.pnut.io/v1/polls/1/response" \ 114 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 115 | -H "Content-Type: application/json" \ 116 | -d "{\"positions\": [1,3]}" \ 117 | -X PUT \ 118 | -H "X-Pretty-Json: 1" 119 | ``` 120 | 121 | Returns poll on success 122 | 123 | ```json 124 | { 125 | "meta": { 126 | "code": 200 127 | }, 128 | "data": {"....Poll Object..."} 129 | } 130 | ``` 131 | 132 | 133 | 134 | ## DELETE /polls/{poll_id} {#delete-polls-id .endpoint} 135 | 136 | Token: user 137 | 138 | Scope: polls 139 | 140 | Delete a poll. This will not disassociate a poll with any other objects (posts, messages...). 141 | 142 | ### URL Parameters 143 | 144 | Name|Description 145 | -|- 146 | `poll_id`|ID of the poll to delete 147 | 148 | ##### Example {.example-code} 149 | 150 | ```bash 151 | curl "https://api.pnut.io/v1/polls/72" \ 152 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 153 | -X DELETE \ 154 | -H "X-Pretty-Json: 1" 155 | ``` 156 | 157 | Returns the deleted poll 158 | 159 | ```json 160 | { 161 | "meta": { 162 | "code": 200 163 | }, 164 | "data": {"...Poll Object...."} 165 | } 166 | ``` 167 | -------------------------------------------------------------------------------- /resources/polls/lookup.md: -------------------------------------------------------------------------------- 1 | # Poll Lookup 2 | 3 | Endpoints: 4 | 5 | * [Get a poll](#get-polls-id) 6 | * [Get multiple polls](#get-polls) 7 | * [Get the authenticated user's polls](#get-users-me-polls) 8 | * [Get responses to the authenticated user's polls](#get-users-me-polls-responses) 9 | 10 | 11 | ## GET /polls/{poll_id} {#get-polls-id .endpoint} 12 | 13 | Token: user 14 | 15 | Scope: polls 16 | 17 | Retrieve a poll object. 18 | 19 | ### URL Parameters 20 | 21 | Name|Description 22 | -|- 23 | `poll_id`|ID of the requested poll 24 | 25 | ### Query Parameters 26 | 27 | Name|Description 28 | -|- 29 | `poll_token`|Required on private polls when you don't know or aren't guaranteed access 30 | 31 | ##### Example {.example-code} 32 | 33 | ```bash 34 | curl "https://api.pnut.io/v1/polls/1" \ 35 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 36 | -H "X-Pretty-Json: 1" 37 | ``` 38 | 39 | Returns the requested poll details 40 | 41 | ```json 42 | { 43 | "meta": { 44 | "code": 200 45 | }, 46 | "data": {"...Poll Object..."} 47 | } 48 | ``` 49 | 50 | 51 | 52 | ## GET /polls {#get-polls .endpoint} 53 | 54 | Token: user 55 | 56 | Scope: polls 57 | 58 | Retrieve a list of specified poll objects. Only returns the first 100 found. 59 | 60 | If the polls need a `poll_token` to access, you may include them with query parameters in the pattern `?poll_token_{poll_id}=xxx&poll_token_{poll_id}=xxx`. 61 | 62 | ### Query String Parameters 63 | 64 | Name|Description 65 | -|- 66 | `ids`|Comma-separated list of poll IDs 67 | 68 | ##### Example {.example-code} 69 | 70 | ```bash 71 | curl "https://api.pnut.io/v1/polls?ids=1,12" \ 72 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 73 | -H "X-Pretty-Json: 1" 74 | ``` 75 | 76 | Returns a list of polls 77 | 78 | ```json 79 | { 80 | "meta": { 81 | "code": 200 82 | }, 83 | "data": [ 84 | {"...Poll Object..."}, 85 | {"...Poll Object..."} 86 | ] 87 | } 88 | ``` 89 | 90 | 91 | 92 | ## GET /users/me/polls {#get-users-me-polls .endpoint} 93 | 94 | Token: user 95 | 96 | Scope: polls 97 | 98 | Retrieve a list of polls created by the authenticated user. 99 | 100 | ##### Example {.example-code} 101 | 102 | ```bash 103 | curl "https://api.pnut.io/v1/users/me/polls" \ 104 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 105 | -H "X-Pretty-Json: 1" 106 | ``` 107 | 108 | Returns a list of polls 109 | 110 | ```json 111 | { 112 | "meta": { 113 | "more": false, 114 | "code": 200, 115 | "min_id": "0", 116 | "max_id": "0" 117 | }, 118 | "data": [ 119 | {"...Poll Object..."}, 120 | {"...Poll Object..."} 121 | ] 122 | } 123 | ``` 124 | 125 | 126 | ## GET /users/me/polls/responses {#get-users-me-polls-responses .endpoint} 127 | 128 | Token: user 129 | 130 | Scope: polls 131 | 132 | Retrieve a list of responses to polls created by the authenticated user. This is a parallel to the `poll_response` user interaction. 133 | 134 | ### Query String Parameters 135 | 136 | Name|Description 137 | -|- 138 | `ids`|Optional comma-separated list of poll IDs. If included, only poll responses to these polls will be returned 139 | 140 | ##### Example {.example-code} 141 | 142 | ```bash 143 | curl "https://api.pnut.io/v1/users/me/polls/responses" \ 144 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 145 | -H "X-Pretty-Json: 1" 146 | ``` 147 | 148 | Returns a list of polls 149 | 150 | ```json 151 | { 152 | "meta": { 153 | "more": false, 154 | "code": 200, 155 | "min_id": "0", 156 | "max_id": "0" 157 | }, 158 | "data": [ 159 | {"...Abbreviated Poll Object..."}, 160 | {"...Abbreviated Poll Object..."} 161 | ] 162 | } 163 | ``` -------------------------------------------------------------------------------- /resources/polls/search.md: -------------------------------------------------------------------------------- 1 | # Poll Search 2 | 3 | Endpoints: 4 | 5 | * [Search polls](#get-polls-search) 6 | 7 | 8 | ## GET /polls/search {#get-polls-search .endpoint} 9 | 10 | Scope: none 11 | 12 | Retrieve a list of polls filtered by the given criteria. 13 | 14 | ### Query Parameters 15 | 16 | #### Sort 17 | 18 | Name|Description 19 | -|- 20 | `order`|One of id or closed_at. Default is by ID 21 | 22 | #### Filter 23 | 24 | Name|Description 25 | -|- 26 | `created_after`|ISO 8601-formatted timestamp after which polls were created 27 | `created_before`|ISO 8601-formatted timestamp before which polls were created 28 | `creator_id`|Only include polls created by this user ID 29 | `exclude_poll_types`|Comma-separated list of poll types to exclude 30 | `file_id`|Matches with this file attached 31 | `is_anonymous`|`1` or `0` to only include anonymous polls 32 | `is_closed`|`1` or `0` to only include closed polls 33 | `is_public`|`1` or `0` to only include public polls 34 | `poll_types`|Comma-separated list of poll types to include 35 | `raw_types`|Comma-separated list of attached raw types. Any matches returned 36 | `you_responded`|If true, only include polls you have responded to 37 | 38 | ##### Example {.example-code} 39 | 40 | ```bash 41 | curl "https://api.pnut.io/v1/polls/search?is_public=1&you_responded=0&is_closed=0" \ 42 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 43 | -H "X-Pretty-Json: 1" 44 | ``` 45 | 46 | Returns a list of polls 47 | 48 | ```json 49 | { 50 | "meta": { 51 | "more": false, 52 | "code": 200, 53 | "min_id": "0", 54 | "max_id": "0" 55 | }, 56 | "data": [ 57 | {"...Poll Object..."} 58 | ] 59 | } 60 | ``` -------------------------------------------------------------------------------- /resources/posts/bookmarks.md: -------------------------------------------------------------------------------- 1 | # Post Bookmarks 2 | 3 | Endpoints: 4 | 5 | * [Get a user's bookmarks](#get-users-id-bookmarks) 6 | * [Bookmark a post](#put-posts-id-bookmark) 7 | * [Delete a bookmark](#delete-posts-id-bookmark) 8 | 9 | Bookmarking is an action for users to keep track of posts. You can see others' bookmarks as well. 10 | 11 | 12 | ## GET /users/{user_id}/bookmarks {#get-users-id-bookmarks .endpoint} 13 | 14 | Token: user 15 | 16 | Scope: any 17 | 18 | Retrieve a list of bookmarks made by the specified user. 19 | 20 | Returned posts may include a `note` string field if looking up bookmarks made by the authorized user. 21 | 22 | ### URL Parameters 23 | 24 | Name|Description 25 | -|- 26 | `user_id`|What user's bookmarks to look up 27 | 28 | ##### Example {.example-code} 29 | 30 | ```bash 31 | curl "https://api.pnut.io/v1/users/1/bookmarks" \ 32 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 33 | -H "X-Pretty-Json: 1" 34 | ``` 35 | 36 | Returns a list of posts. 37 | 38 | ```json 39 | { 40 | "meta": { 41 | "more": false, 42 | "max_id": "0", 43 | "min_id": "0", 44 | "code": 200 45 | }, 46 | "data": [ 47 | {"...Post Object..."} 48 | ] 49 | } 50 | ``` 51 | 52 | 53 | ## PUT /posts/{post_id}/bookmark {#put-posts-id-bookmark .endpoint} 54 | 55 | Token: user 56 | 57 | Scope: write_post 58 | 59 | Bookmark a post. 60 | 61 | ### URL Parameters 62 | 63 | Name|Description 64 | -|- 65 | `post_id`|Post to bookmark 66 | 67 | ### PUT Body Data 68 | 69 | Name|Description 70 | -|- 71 | `note`|Optional 128-character note that will only be visible when a user retrieves their own bookmarks. It is not escaped. 72 | 73 | ##### Example {.example-code} 74 | 75 | ```bash 76 | curl "https://api.pnut.io/v1/posts/2375/bookmark" \ 77 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 78 | -H "Content-Type: application/json" \ 79 | -d "{\"note\": \"Robert has great posts.\"}" \ 80 | -X PUT \ 81 | -H "X-Pretty-Json: 1" 82 | ``` 83 | 84 | Returns the bookmarked post. 85 | 86 | ```json 87 | { 88 | "meta": { 89 | "code": 200 90 | }, 91 | "data": {"...Post Object..."} 92 | } 93 | ``` 94 | 95 | 96 | ## DELETE /posts/{post_id}/bookmark {#delete-posts-id-bookmark .endpoint} 97 | 98 | Token: user 99 | 100 | Scope: write_post 101 | 102 | Delete a bookmark. 103 | 104 | ### URL Parameters 105 | 106 | Name|Description 107 | -|- 108 | `post_id`|Post to delete a bookmark for 109 | 110 | ##### Example {.example-code} 111 | 112 | ```bash 113 | curl "https://api.pnut.io/v1/posts/2375/bookmark" \ 114 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 115 | -X DELETE \ 116 | -H "X-Pretty-Json: 1" 117 | ``` 118 | 119 | Returns the post a bookmark was removed from. 120 | 121 | ```json 122 | { 123 | "meta": { 124 | "code": 200 125 | }, 126 | "data": {"....Post Object..."} 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /resources/posts/explore.md: -------------------------------------------------------------------------------- 1 | # Explore Streams 2 | 3 | Endpoints: 4 | 5 | * [Get a list of explore streams](#get-posts-streams-explore) 6 | * [Get an explore stream](#get-posts-streams-explore-slug) 7 | 8 | Explore streams are basically pre-built searches with some simple criteria. 9 | 10 | 11 | ## GET /posts/streams/explore {#get-posts-streams-explore .endpoint} 12 | 13 | Scope: none 14 | 15 | Retrieve a list of explore streams. 16 | 17 | ##### Example {.example-code} 18 | 19 | ```bash 20 | curl "https://api.pnut.io/v1/posts/streams/explore" \ 21 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 22 | -H "X-Pretty-Json: 1" 23 | ``` 24 | 25 | Returns a list of explore streams. 26 | 27 | ```json 28 | { 29 | "meta": { 30 | "code": 200 31 | }, 32 | "data": [ 33 | { 34 | "description": "String", 35 | "slug": "String", 36 | "title": "String", 37 | "url": "https://example.com" 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | 44 | ## GET /posts/streams/explore/{slug} {#get-posts-streams-explore-slug .endpoint} 45 | 46 | Scope: none 47 | 48 | Retrieve a list of posts in an explore stream. 49 | 50 | ### URL Parameters 51 | 52 | Name|Description 53 | -|- 54 | `slug`|Slug of the stream to retrieve posts from. Retrieve slugs to use from the previous call. 55 | 56 | ##### Example {.example-code} 57 | 58 | ```bash 59 | curl "https://api.pnut.io/v1/posts/streams/explore/conversations?count=1" \ 60 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 61 | -H "X-Pretty-Json: 1" 62 | ``` 63 | 64 | Returns a list of posts. 65 | 66 | ```json 67 | { 68 | "meta": { 69 | "more": true, 70 | "max_id": "0", 71 | "min_id": "0", 72 | "code": 200 73 | }, 74 | "data": [ 75 | {"...Post Object..."} 76 | ] 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /resources/posts/interactions.md: -------------------------------------------------------------------------------- 1 | # Post Interactions 2 | 3 | Endpoints: 4 | 5 | * [Get interactions made against a post](#get-posts-id-interactions) 6 | 7 | This endpoint mirrors [User Interactions](/docs/resources/users/interactions), but for a single post, instead of a user, and it does not include `objects`. 8 | 9 | 10 | ## GET /posts/{post_id}/interactions {#get-posts-id-interactions .endpoint} 11 | 12 | Scope: none 13 | 14 | Retrieve actions executed against a post. 15 | 16 | ### URL Parameters 17 | 18 | Name|Description 19 | -|- 20 | `post_id`|ID of the post to retrieve actions for 21 | 22 | ### Query Parameters 23 | 24 | Name|Description 25 | -|- 26 | `filters`|Comma-separated list of actions to filter by. Allowed: `bookmark`, `repost`, `reply`. 27 | `exclude`|Comma-separated list of actions to exclude. `?exclude=bookmark` will return all actions except bookmarks. If `filters` is also specified, this is ignored. 28 | 29 | ##### Example {.example-code} 30 | 31 | ```bash 32 | curl "https://api.pnut.io/v1/posts/83/interactions" \ 33 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 34 | -H "X-Pretty-Json: 1" 35 | ``` 36 | 37 | Returns a list of interactions. 38 | 39 | ```json 40 | { 41 | "meta": { 42 | "more": false, 43 | "max_id": "0", 44 | "min_id": "0", 45 | "code": 200 46 | }, 47 | "data": [ 48 | { 49 | "pagination_id": "0", 50 | "event_date": "ISO 8601", 51 | "action": "String", 52 | "users": [ 53 | {"...User Object..."} 54 | ] 55 | } 56 | ] 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /resources/posts/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Post Lifecycle 2 | 3 | Endpoints: 4 | 5 | * [Create a post](#post-posts) 6 | * [Revise a post](#put-posts-id) 7 | * [Delete a post](#delete-posts-id) 8 | 9 | 10 | ## POST /posts {#post-posts .endpoint} 11 | 12 | Token: user 13 | 14 | Scope: write_post 15 | 16 | Create a post. 17 | 18 | On creation, you can automatically update the "personal" [stream marker](../stream-marker#post-markers) to the post's ID by including `update_marker=1` in the query string. 19 | 20 | Posts from the same human- or feed-type user cannot contain the same `text` within 120 seconds. 21 | 22 | An `application/json` Content-Type is preferred over form. Normal links and markdown links are parsed by the server by default. 23 | 24 | ### POST Body Data 25 | 26 | Name|Description 27 | -|- 28 | `text`|__Required__ 256 character-limited string 29 | `entities.parse_links`|Boolean whether the links should be parsed by the server. Default `true` 30 | `entities.parse_markdown_links`|Boolean whether the markdown links should be parsed by the server. Default `true` 31 | `is_nsfw`|Boolean whether the post should be marked as "NSFW" (Not Safe For Work/mature/offensive). Including the tag `#nsfw` in the post body will mark a post NSFW unless overridden by this. 32 | `raw`|Embedded [raw values](/docs/implementation/raw). 33 | `reply_to`|ID of another post to reply to 34 | 35 | ##### Example {.example-code} 36 | 37 | ```bash 38 | curl "https://api.pnut.io/v1/posts" \ 39 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 40 | -H "Content-Type: application/json" \ 41 | -d "{\"text\": \"The people here are not shy.\"}" \ 42 | -X POST \ 43 | -H "X-Pretty-Json: 1" 44 | ``` 45 | 46 | Returns the created post. 47 | 48 | ```json 49 | { 50 | "meta": { 51 | "code": 201 52 | }, 53 | "data": {"....Post Object..."} 54 | } 55 | ``` 56 | 57 | 58 | ## PUT /posts/{post_id} {#put-posts-id .endpoint} 59 | 60 | Token: user 61 | 62 | Scope: write_post 63 | 64 | Edit or "revise" a post. 65 | 66 | * Can only be done within __300 seconds__ of the original post's creation 67 | * Can only be done __once__ to a post 68 | * Must contain the same [entities](../../implementation/entities) that were in the original post (Positions can change. Links can be formatted in any way, but the URLs have to be the same) 69 | 70 | Once a revision has been made, the original post can still be retrieved from the [Revisions endpoint](lookup#get-posts-id-revisions). 71 | 72 | Reposts made before the revision will continue to point at the original post. 73 | 74 | ### URL Parameters 75 | 76 | Name|Description 77 | -|- 78 | `post_id`|ID of the post to revise 79 | 80 | The POST body can be the same as when creating a post. `is_nsfw` *can* be changed from an initial post to a revision. 81 | 82 | ##### Example {.example-code} 83 | 84 | ```bash 85 | curl "https://api.pnut.io/v1/posts/2392" \ 86 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 87 | -H "Content-Type: application/json" \ 88 | -d "{\"text\": \"system of a down\"}" \ 89 | -X PUT \ 90 | -H "X-Pretty-Json: 1" 91 | ``` 92 | 93 | Returns the revised post. 94 | 95 | ```json 96 | { 97 | "meta": { 98 | "code": 201 99 | }, 100 | "data": {"...Post Object..."} 101 | } 102 | ``` 103 | 104 | 105 | ## DELETE /posts/{post_id} {#delete-posts-id .endpoint} 106 | 107 | Token: user 108 | 109 | Scope: write_post 110 | 111 | Delete a post. 112 | 113 | ### URL Parameters 114 | 115 | Name|Description 116 | -|- 117 | `post_id`|ID of the post to delete 118 | 119 | ##### Example {.example-code} 120 | 121 | ```bash 122 | curl "https://api.pnut.io/v1/posts/2392" \ 123 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 124 | -X DELETE \ 125 | -H "X-Pretty-Json: 1" 126 | ``` 127 | 128 | Returns the deleted post. 129 | 130 | ```json 131 | { 132 | "meta": { 133 | "code": 200 134 | }, 135 | "data": {"...Post Object...."} 136 | } 137 | ``` -------------------------------------------------------------------------------- /resources/posts/lookup.md: -------------------------------------------------------------------------------- 1 | # Post Lookup 2 | 3 | Endpoints: 4 | 5 | * [Get a post](#get-posts-id) 6 | * [Get multiple posts](#get-posts) 7 | * [Get post revisions](#get-posts-id-revisions) 8 | 9 | 10 | ## GET /posts/{post_id} {#get-posts-id .endpoint} 11 | 12 | Scope: none 13 | 14 | Retrieve a post object. 15 | 16 | ### URL Parameters 17 | 18 | Name|Description 19 | -|- 20 | `post_id`|Post to retrieve 21 | 22 | ##### Example {.example-code} 23 | 24 | ```bash 25 | curl "https://api.pnut.io/v1/posts/20" \ 26 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 27 | -H "X-Pretty-Json: 1" 28 | ``` 29 | 30 | Returns a post 31 | 32 | ```json 33 | { 34 | "meta": { 35 | "code": 200 36 | }, 37 | "data": {"...Post Object..."} 38 | } 39 | ``` 40 | 41 | 42 | ## GET /posts {#get-posts .endpoint} 43 | 44 | Scope: none 45 | 46 | Retrieve a list of specified post objects. Only retrieves the first 200 found. 47 | 48 | ### Query String Parameters 49 | 50 | Name|Description 51 | -|- 52 | `ids`|Comma-separated list of post IDs. 53 | 54 | ##### Example {.example-code} 55 | 56 | ```bash 57 | curl "https://api.pnut.io/v1/posts?ids=20,21,4" \ 58 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 59 | -H "X-Pretty-Json: 1" 60 | ``` 61 | 62 | Returns a list of posts 63 | 64 | ```json 65 | { 66 | "meta": { 67 | "code": 200 68 | }, 69 | "data": [ 70 | {"...Post Object..."}, 71 | {"...Post Object..."}, 72 | {"...Post Object..."} 73 | ] 74 | } 75 | ``` 76 | 77 | 78 | ## GET /posts/{post_id}/revisions {#get-posts-id-revisions .endpoint} 79 | 80 | Scope: none 81 | 82 | Retrieve a list of [previous versions](lifecycle#put-posts-id) of a post, not including the most recent. Currently a post can only have one previous version. 83 | 84 | Revisions returned will have `revision` as a String number indicating which version of the post it is. Revisions start at `"0"`. 85 | 86 | ### URL Parameters 87 | 88 | Name|Description 89 | -|- 90 | `post_id`|Post ID to retrieve any revisions of 91 | 92 | ##### Example {.example-code} 93 | 94 | ```bash 95 | curl "https://api.pnut.io/v1/posts/2392/revisions" \ 96 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 97 | -H "X-Pretty-Json: 1" 98 | ``` 99 | 100 | Returns a list of posts 101 | 102 | ```json 103 | { 104 | "meta": { 105 | "code": 200 106 | }, 107 | "data": [ 108 | {"....Post Object..."} 109 | ] 110 | } 111 | ``` -------------------------------------------------------------------------------- /resources/posts/report.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | Endpoints: 4 | 5 | * [Report a post](#post-posts-id-report) 6 | 7 | These are the current reasons that will be honored for reporting: 8 | 9 | * `account_type`: posting in a behavior counter to the purposes of [account types](https://pnut.io/about/account-types) 10 | * `nsfw`: unflagged mature material according to [the community guidelines](https://pnut.io/about/mature-content) 11 | * `soliciting`: unwelcome soliciting 12 | * `user_abuse`: use of the API or network to abuse another user 13 | 14 | 15 | ## POST /posts/{post_id}/report {#post-posts-id-report .endpoint} 16 | 17 | Token: user 18 | 19 | Scope: any 20 | 21 | Report a post for abuse. 22 | 23 | To test this endpoint, report a post by user [@testuser](/docs/resources/testuser). 24 | 25 | ### URL Parameters 26 | 27 | Name|Description 28 | -|- 29 | `post_id`|ID of the post to report. 30 | 31 | ### POST Body Data 32 | 33 | Name|Description 34 | -|- 35 | `reason`|One of: `account_type`, `nsfw`, `soliciting`, `user_abuse`. 36 | 37 | ##### Example {.example-code} 38 | 39 | ```bash 40 | curl "https://api.pnut.io/v1/posts/381576/report" \ 41 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 42 | -H "Content-Type: application/json" \ 43 | -D "{\"reason\": \"account_type\"}" \ 44 | -X POST \ 45 | -H "X-Pretty-Json: 1" 46 | ``` 47 | 48 | Returns a 201 49 | 50 | ```json 51 | 52 | ``` 53 | -------------------------------------------------------------------------------- /resources/posts/reposts.md: -------------------------------------------------------------------------------- 1 | # Reposts 2 | 3 | Endpoints: 4 | 5 | * [Create a Repost](#put-posts-id-repost) 6 | * [Delete a repost](#delete-posts-id-repost) 7 | 8 | Reposts are special posts. You cannot reply to, repost, or bookmark a repost, but the API will instead reply to, repost, or bookmark the original post that was reposted. 9 | 10 | Even if a post is revised, existing reposts will still reflect the acted-on post's original contents, not the revision. 11 | 12 | __Contents__ 13 | 14 | Reposts act as complete posts in themselves, with the "original" post embedded as an additional object in the `repost_of` field. The `contents`, `counts`, `raw`, `is_revised`, and `is_nsfw` fields of the post will mirror the acted-on post. 15 | 16 | __Deletion__ 17 | 18 | When a repost is deleted, it will henceforth return a `404 Not Found`, instead of returning a normal post with `is_deleted: true`. When the acted-on post is deleted, all reposts of it will also be deleted. 19 | 20 | __Return Values__ 21 | 22 | When creating or deleting a repost, the API returns the acted-on post, not the newly created post. This is the same as when bookmarking a post. 23 | 24 | 25 | ## PUT /posts/{post_id}/repost {#put-posts-id-repost .endpoint} 26 | 27 | Token: user 28 | 29 | Scope: write_post 30 | 31 | Repost another post. The repost will show up in followers' streams if they have not seen another repost of the same within the last week, and if the reposted post is not in their recent stream. It is created in its own thread, not the thread of the original post. This increments a user's post count. 32 | 33 | Reposts can be marked as NSFW just like a normal post, but will always identify as NSFW if the embedded original post was marked as NSFW. The "repost" will be marked NSFW, but the embedded original post will be embedded however it was created. So a client can see that difference if it is useful. 34 | 35 | ### URL Parameters 36 | 37 | Name|Description 38 | -|- 39 | `post_id`|ID of the post to repost 40 | 41 | ### POST Body Data 42 | 43 | Name|Description 44 | -|- 45 | `is_nsfw`|If true, the post will be marked as "NSFW" (Not Safe For Work/mature/offensive). 46 | 47 | ##### Example {.example-code} 48 | 49 | ```bash 50 | curl "https://api.pnut.io/v1/posts/2370/repost" \ 51 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 52 | -X PUT \ 53 | -H "X-Pretty-Json: 1" 54 | ``` 55 | 56 | Returns the original post that has been reposted. 57 | 58 | ```json 59 | { 60 | "meta": { 61 | "code": 201 62 | }, 63 | "data": {"...Post object..."} 64 | } 65 | ``` 66 | 67 | 68 | ## DELETE /posts/{post_id}/repost {#delete-posts-id-repost .endpoint} 69 | 70 | Token: user 71 | 72 | Scope: write_post 73 | 74 | Delete a repost. The actual repost is completely deleted; it does not leave behind a thread or deleted post to look up. 75 | 76 | Users can also delete their own reposts even if they no longer have access to the original reposted post (e.g., they were blocked). In that case, the returned post in the `data` field is simply `{"id":POST_ID,"you_reposted":false}`. 77 | 78 | ### URL Parameters 79 | 80 | Name|Description 81 | -|- 82 | `post_id`|ID of the post to delete a repost for 83 | 84 | ##### Example {.example-code} 85 | 86 | ```bash 87 | curl "https://api.pnut.io/v1/posts/2370/repost" \ 88 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 89 | -X DELETE \ 90 | -H "X-Pretty-Json: 1" 91 | ``` 92 | 93 | Returns the original post that was previously reposted. 94 | 95 | ```json 96 | { 97 | "meta": { 98 | "code": 200 99 | }, 100 | "data": {"....Post object..."} 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /resources/posts/search.md: -------------------------------------------------------------------------------- 1 | # Post Search 2 | 3 | Endpoints: 4 | 5 | * [Search posts](#get-posts-search) 6 | 7 | 8 | ## GET /posts/search {#get-posts-search .endpoint} 9 | 10 | Scope: none 11 | 12 | Retrieve a list of posts filtered by the given criteria. 13 | 14 | ### Query Parameters 15 | 16 | #### Search 17 | 18 | Name|Description 19 | -|- 20 | `q`|List of words included in posts 21 | 22 | #### Sort 23 | 24 | Name|Description 25 | -|- 26 | `order`|One of id or relevance. Default is by relevance 27 | 28 | #### Filter 29 | 30 | Name|Description 31 | -|- 32 | `client_id`|Only include posts created by this client ID 33 | `created_after`|ISO 8601-formatted timestamp after which posts were created 34 | `created_before`|ISO 8601-formatted timestamp before which posts were created 35 | `creator_id`|Only include posts created by this user ID 36 | `exclude_user_ids`|Comma-separated list of user IDs to exclude from results 37 | `file_id`|Matches with this file attached 38 | `file_kinds`|Comma-separated list of oEmbed-attached file types (`video`, `audio`, `image`, `other`) 39 | `has_mentions`|`1` or `0` to include posts with or without mentions. Excludes other mentions filters below 40 | `is_directed`|If `1`, only include [directed posts](../../implementation/entities#leading-mentions) 41 | `is_nsfw`|If `0`, does not include NSFW posts 42 | `is_reply`|`1` or `0` to include messages that are or are not replies 43 | `is_revised`|If `1`, only include revised posts 44 | `leading_mentions`|Comma-separated list of mentions at the beginning of a post. Any matches returned 45 | `mentions`|Comma-separated list of mentions. Any matches returned 46 | `poll_id`|Matches with this poll attached 47 | `raw_types`|Comma-separated list of attached raw types. Any matches returned 48 | `reply_to`|Only include posts replying to this post 49 | `tags`|Comma-separated list of tags. Any matches returned 50 | `thread_id`|Only include posts in this thread 51 | `url_domains`|Comma-separated list of domains. Any matches returned. Do not include `http://` or `www` in front of domain 52 | `urls`|Comma-separated list of URLs. Any matches returned 53 | `user_types`|Comma-separated list of user types of: human, feed, bot 54 | 55 | ##### Example {.example-code} 56 | 57 | ```bash 58 | curl "https://api.pnut.io/v1/posts/search?tags=mndp,MondayNightDanceParty" \ 59 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 60 | -H "X-Pretty-Json: 1" 61 | ``` 62 | 63 | Returns a list of posts 64 | 65 | ```json 66 | { 67 | "meta": { 68 | "more": true, 69 | "max_id": "0", 70 | "min_id": "0", 71 | "code": 200 72 | }, 73 | "data": [ 74 | {"...Post Object..."}, 75 | {"...Post Object..."} 76 | ] 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /resources/posts/streams.md: -------------------------------------------------------------------------------- 1 | # Post Streams 2 | 3 | Endpoints: 4 | 5 | * [Get a user's personal stream](#get-posts-streams-me) 6 | * [Get a user's unified personal stream](#get-posts-streams-unified) 7 | * [Get mentions of a user](#get-users-id-mentions) 8 | * [Get posts by a user](#get-users-id-posts) 9 | * [Get the global stream](#get-posts-streams-global) 10 | * [Get posts with a tag](#get-posts-tags-tag) 11 | 12 | 13 | ## GET /posts/streams/me {#get-posts-streams-me .endpoint} 14 | 15 | Token: user 16 | 17 | Scope: stream 18 | 19 | The authenticated user's stream of posts from their followers and themself. 20 | 21 | ##### Example {.example-code} 22 | 23 | ```bash 24 | curl "https://api.pnut.io/v1/posts/streams/me?count=2" \ 25 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 26 | -H "X-Pretty-Json: 1" 27 | ``` 28 | 29 | Returns a list of posts. 30 | 31 | ```json 32 | { 33 | "meta": { 34 | "more": true, 35 | "max_id": "0", 36 | "min_id": "0", 37 | "code": 200 38 | }, 39 | "data": [ 40 | {"...Post Object..."}, 41 | {"...Post Object..."} 42 | ] 43 | } 44 | ``` 45 | 46 | 47 | ## GET /posts/streams/unified {#get-posts-streams-unified .endpoint} 48 | 49 | Token: user 50 | 51 | Scope: stream 52 | 53 | A combined Personal Stream including the authenticated user's mentions. 54 | 55 | ##### Example {.example-code} 56 | 57 | ```bash 58 | curl "https://api.pnut.io/v1/posts/streams/unified?count=1" \ 59 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 60 | -H "X-Pretty-Json: 1" 61 | ``` 62 | 63 | Returns a list of posts. 64 | 65 | ```json 66 | { 67 | "meta": { 68 | "more": true, 69 | "max_id": "0", 70 | "min_id": "0", 71 | "code": 200 72 | }, 73 | "data": [ 74 | {"...Post Object..."} 75 | ] 76 | } 77 | ``` 78 | 79 | 80 | ## GET /users/{user_id}/mentions {#get-users-id-mentions .endpoint} 81 | 82 | Scope: none 83 | 84 | Posts mentioning the specified user. 85 | 86 | ### URL Parameters 87 | 88 | Name|Description 89 | -|- 90 | `user_id`|Posts mention this user 91 | 92 | ##### Example {.example-code} 93 | 94 | ```bash 95 | curl "https://api.pnut.io/v1/users/9/mentions?count=2" \ 96 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 97 | -H "X-Pretty-Json: 1" 98 | ``` 99 | 100 | Returns a list of posts. 101 | 102 | ```json 103 | { 104 | "meta": { 105 | "more": true, 106 | "max_id": "0", 107 | "min_id": "0", 108 | "code": 200 109 | }, 110 | "data": [ 111 | {"....Post Object..."}, 112 | {"....Post Object..."} 113 | ] 114 | } 115 | ``` 116 | 117 | 118 | ## GET /users/{user_id}/posts {#get-users-id-posts .endpoint} 119 | 120 | Scope: none 121 | 122 | Posts created by the specified user. 123 | 124 | If a user looks up a user they blocked or muted, the posts will still be retrieved. 125 | 126 | ### URL Parameters 127 | 128 | Name|Description 129 | -|- 130 | `user_id`|Posts created by this user 131 | 132 | ##### Example {.example-code} 133 | 134 | ```bash 135 | curl "https://api.pnut.io/v1/users/9/posts?count=2" \ 136 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 137 | -H "X-Pretty-Json: 1" 138 | ``` 139 | 140 | Returns a list of posts. 141 | 142 | ```json 143 | { 144 | "meta": { 145 | "more": true, 146 | "max_id": "0", 147 | "min_id": "0", 148 | "code": 200 149 | }, 150 | "data": [ 151 | {"...Post Object...."}, 152 | {"...Post Object...."} 153 | ] 154 | } 155 | ``` 156 | 157 | 158 | ## GET /posts/streams/global {#get-posts-streams-global .endpoint} 159 | 160 | Scope: none 161 | 162 | A stream of all `human`-type users' public posts. 163 | 164 | ##### Example {.example-code} 165 | 166 | ```bash 167 | curl "https://api.pnut.io/v1/posts/streams/global?count=2" \ 168 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 169 | -H "X-Pretty-Json: 1" 170 | ``` 171 | 172 | Returns a list of posts. 173 | 174 | ```json 175 | { 176 | "meta": { 177 | "more": true, 178 | "max_id": "0", 179 | "min_id": "0", 180 | "code": 200 181 | }, 182 | "data": [ 183 | {"...Post Object.."}, 184 | {"...Post Object.."} 185 | ] 186 | } 187 | ``` 188 | 189 | 190 | ## GET /posts/tags/{tag} {#get-posts-tags-tag .endpoint} 191 | 192 | Scope: none 193 | 194 | A stream of all posts that include the specified `tag`. 195 | 196 | ### URL Parameters 197 | 198 | Name|Description 199 | -|- 200 | `tag`|The string tag that all posts should include 201 | 202 | ##### Example {.example-code} 203 | 204 | ```bash 205 | curl "https://api.pnut.io/v1/posts/tags/MondayNightDanceParty?count=2" \ 206 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 207 | -H "X-Pretty-Json: 1" 208 | ``` 209 | 210 | Returns a list of posts. 211 | 212 | ```json 213 | { 214 | "meta": { 215 | "more": true, 216 | "max_id": "0", 217 | "min_id": "0", 218 | "code": 200 219 | }, 220 | "data": [ 221 | {"....Post Object...."}, 222 | {"....Post Object...."} 223 | ] 224 | } 225 | ``` -------------------------------------------------------------------------------- /resources/posts/threads.md: -------------------------------------------------------------------------------- 1 | # Post Threads 2 | 3 | Endpoints: 4 | 5 | * [Get posts in a thread](#get-posts-id-thread) 6 | 7 | Post thread is determined by the post that is not a reply. A reply to a reply will not create a new "thread" in the API. 8 | 9 | *If looking for how to retrieve only replies to a specific post, use [post search](search).* 10 | 11 | 12 | ## GET /posts/{post_id}/thread {#get-posts-id-thread .endpoint} 13 | 14 | Scope: none 15 | 16 | Retrieve posts within a thread. Threads are separated by what root post all posts below it have replied to. 17 | 18 | ### URL Parameters 19 | 20 | Name|Description 21 | -|- 22 | `post_id`|ID of the post to retrieve the thread for 23 | 24 | ##### Example {.example-code} 25 | 26 | ```bash 27 | curl "https://api.pnut.io/v1/posts/18/thread" \ 28 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 29 | -H "X-Pretty-Json: 1" 30 | ``` 31 | 32 | Returns a list of posts from the thread. 33 | 34 | ```json 35 | { 36 | "meta": { 37 | "more": false, 38 | "max_id": "0", 39 | "min_id": "0", 40 | "code": 200 41 | }, 42 | "data": [ 43 | {"...Post Object..."} 44 | ] 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /resources/stream-marker.md: -------------------------------------------------------------------------------- 1 | # Stream Marker 2 | 3 | Endpoints: 4 | 5 | * [Set a list of stream markers](#post-markers) 6 | 7 | Stream markers provide a way to save a user's spot in a stream of posts or messages; a lightweight way to sync between clients, or pick up where you left off without otherwise saving it. 8 | 9 | These are special pagination values for `since_id` and `before_id` that you must use on markable streams to actually read from its marker: 10 | 11 | * `last_read`: the current `last_read_id` of the stream's marker 12 | * `last_read_inclusive` 13 | * `marker`: the current `id` of the stream's marker 14 | * `marker_inclusive` 15 | 16 | Current markable streams/names: 17 | 18 | * [global](posts/streams#get-posts-streams-global): "global" 19 | * [unified](posts/streams#get-posts-streams-unified) and [me](posts/streams#get-posts-streams-me): "personal" 20 | * [mentions](posts/streams#get-users-id-mentions): "mentions" 21 | * [channels](channels/lookup#get-channels-id): "channel:{id}" 22 | 23 | On creation of posts and messages, you can automatically update the "personal" and "channel" markers to the created ID by including `update_marker=1` in the query string. 24 | 25 | 26 | ## POST /markers {#post-markers .endpoint} 27 | 28 | Token: user 29 | 30 | Scope: any 31 | 32 | Post a list of marker objects. You may update up to 10 at once. 33 | 34 | For a channel to be marked "read", the marker `id` must be at or greater than the current most-recent message in it. 35 | 36 | ### URL Parameters 37 | 38 | Name|Description 39 | -|- 40 | `reset_read_id`|`id` indicates the current position of the marker. `last_read_id` indicates the *last* item in the stream that has ever been consumed by the marker. To move that in reverse, this must be set True 41 | 42 | ### POST Body Data 43 | 44 | Name|Description 45 | -|- 46 | `id`|__Required__ The ID of the object in the stream you want to mark as having read most recently 47 | `name`|__Required__ What marker to update for a user. E.g., the global stream is `global`, or a channel would be `channel:{id}` 48 | `percentage`|Can specify 1-100 percent of the current `id` has been read or positioned in a stream. 49 | 50 | ##### Example Call {.example-code} 51 | 52 | ```bash 53 | curl "https://api.pnut.io/v1/markers" \ 54 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 55 | -H "X-Pretty-Json: 1" \ 56 | -H "Content-Type: application/json" \ 57 | -d "[ 58 | { 59 | \"name\": \"channel:12\", 60 | \"id\": \"2593\" 61 | } 62 | ]" 63 | -X POST 64 | ``` 65 | 66 | Returns a list of the updated markers 67 | 68 | ```json 69 | { 70 | "meta": { 71 | "code": 200 72 | }, 73 | "data": [ 74 | { 75 | "id": "0", 76 | "last_read_id": "0", 77 | "percentage": 0, 78 | "updated_at": "ISO-8601", 79 | "version": "String", 80 | "name": "String" 81 | } 82 | ] 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /resources/token.md: -------------------------------------------------------------------------------- 1 | # Token 2 | 3 | Endpoints: 4 | 5 | * [Get the current token](#get-token) 6 | * [Delete the current token](#delete-token) 7 | 8 | Get or delete the current user's authenticated token. 9 | 10 | 11 | ## GET /token {#get-token .endpoint} 12 | 13 | Token: user 14 | 15 | Scope: any 16 | 17 | Retrieve an object with the currently authenticated token, username, and user ID. 18 | 19 | Includes `data.email` if `email` scope is authorized. Includes `markdown_text` in user object. 20 | 21 | ##### Example {.example-code} 22 | 23 | ```bash 24 | curl "https://api.pnut.io/v1/token" \ 25 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 26 | -H "X-Pretty-Json: 1" 27 | ``` 28 | 29 | Returns the requested token 30 | 31 | ```json 32 | { 33 | "meta": { 34 | "code": 200 35 | }, 36 | "data": { 37 | "app": { 38 | "id": "String", 39 | "url": "https://example.com", 40 | "name": "String" 41 | }, 42 | "scopes": [ 43 | "String", 44 | "String", 45 | "String" 46 | ], 47 | "user": {"...User Object..."}, 48 | "storage": { 49 | "available": 0, 50 | "total": 0 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | 57 | ## DELETE /token {#delete-token .endpoint} 58 | 59 | Token: user 60 | 61 | Scope: any 62 | 63 | Delete the currently authenticated token. 64 | 65 | Note that this only deletes the currently authenticated token, and the user will still not be required to reauthorize scopes in the future that have been authorized. For the user to revoke all access tokens for the client, they must do so manually from their account on pnut.io, or all of their tokens for the client must be deleted to reset scopes. 66 | 67 | ##### Example {.example-code} 68 | 69 | ```bash 70 | curl "https://api.pnut.io/v1/token" \ 71 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 72 | -H "X-Pretty-Json: 1" \ 73 | -X DELETE 74 | ``` 75 | 76 | Returns the deleted token 77 | 78 | ```json 79 | { 80 | "meta": { 81 | "code": 200 82 | }, 83 | "data": { 84 | "app": { 85 | "id": "String", 86 | "url": "https://example.com", 87 | "name": "String" 88 | }, 89 | "scopes": [ 90 | "String", 91 | "String", 92 | "String" 93 | ], 94 | "user": {"...User Object...."}, 95 | "storage": { 96 | "available": 0, 97 | "total": 0 98 | } 99 | } 100 | } 101 | ``` 102 | -------------------------------------------------------------------------------- /resources/user-streams.md: -------------------------------------------------------------------------------- 1 | # User Streams 2 | 3 | Endpoints: 4 | 5 | * [Delete a stream](#delete-users-me-streams-conn) 6 | * [Delete a subscription](#delete-users-me-streams-conn-sub) 7 | 8 | User streams are long-lasting connections between the API and user-facing clients, for near-realtime interaction. They are similar to [App Streams](app-streams) but for individual users. 9 | 10 | Look at [How To: User Streams](../how-to/user-streams) for details on usage. 11 | 12 | 13 | ## DELETE /users/me/streams/{connection_id} {#delete-users-me-streams-conn .endpoint} 14 | 15 | Token: user 16 | 17 | Scope: any 18 | 19 | Delete a user stream. 20 | 21 | ##### Example {.example-code} 22 | 23 | ```bash 24 | curl "https://api.pnut.io/v1/users/me/streams/${CONNECTION_ID}" \ 25 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 26 | -X GET \ 27 | -H "X-Pretty-Json: 1" 28 | ``` 29 | 30 | Returns 204. 31 | 32 | ```json 33 | - 34 | ``` 35 | 36 | 37 | ## DELETE /users/me/streams/{connection_id}/{subscription_id} {#delete-users-me-streams-conn-sub .endpoint} 38 | 39 | Token: user 40 | 41 | Scope: any 42 | 43 | Delete a subscription for a user stream. 44 | 45 | ##### Example {.example-code} 46 | 47 | ```bash 48 | curl "https://api.pnut.io/v1/users/me/streams/${CONNECTION_ID}/${SUBSCRIPTION_ID}" \ 49 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 50 | -X GET \ 51 | -H "X-Pretty-Json: 1" 52 | ``` 53 | 54 | Returns 204. 55 | 56 | ```json 57 | -- 58 | ``` 59 | -------------------------------------------------------------------------------- /resources/users/badges.md: -------------------------------------------------------------------------------- 1 | # Badges 2 | 3 | Endpoints: 4 | 5 | * [Get a badge](#get-badges-id) 6 | * [Get all badges](#get-badges) 7 | * [Get the authenticated user's badges](#get-users-me-badges) 8 | 9 | Badges are accessible to users who are [current badge holders](https://pnut.io/about/sign-up). 10 | 11 | A user can set what badge to display on their profile [from their account](https://pnut.io/account/badge), or a client can update it [by updating the user](https://docs.pnut.io/resources/users/profile). 12 | 13 | 14 | ## GET /badges/{badge_id} {#get-badges-id .endpoint} 15 | 16 | Scope: none 17 | 18 | Retrieve a badge object. 19 | 20 | ##### Example {.example-code} 21 | 22 | ```bash 23 | curl "https://api.pnut.io/v1/badges/1" \ 24 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 25 | -H "X-Pretty-Json: 1" 26 | ``` 27 | 28 | Returns the badge object 29 | 30 | ```json 31 | { 32 | "meta": { 33 | "code": 200 34 | }, 35 | "data": { 36 | "id": "0", 37 | "name": "String", 38 | "description": "String" 39 | } 40 | } 41 | ``` 42 | 43 | 44 | ## GET /badges {#get-badges .endpoint} 45 | 46 | Scope: none 47 | 48 | Retrieve a list of all badges. It is not paginated. 49 | 50 | ##### Example {.example-code} 51 | 52 | ```bash 53 | curl "https://api.pnut.io/v1/badges" \ 54 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 55 | -H "X-Pretty-Json: 1" 56 | ``` 57 | 58 | Returns a list of badges 59 | 60 | ```json 61 | { 62 | "meta": { 63 | "code": 200 64 | }, 65 | "data": [ 66 | { 67 | "id": "0", 68 | "name": "String", 69 | "description": "String" 70 | } 71 | ] 72 | } 73 | ``` 74 | 75 | 76 | ## GET /users/me/badges {#get-users-me-badges .endpoint} 77 | 78 | Token: user 79 | 80 | Scope: any 81 | 82 | Retrieve a list of all badges for the authenticated user. It is not paginated. 83 | 84 | ##### Example {.example-code} 85 | 86 | ```bash 87 | curl "https://api.pnut.io/v1/users/me/badges" \ 88 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 89 | -H "X-Pretty-Json: 1" 90 | ``` 91 | 92 | Returns a list of badges in order of most recently awarded. 93 | 94 | ```json 95 | { 96 | "meta": { 97 | "code": 200 98 | }, 99 | "data": [ 100 | { 101 | "id": "0", 102 | "name": "String", 103 | "description": "String", 104 | "awarded_at": "ISO 8601" 105 | } 106 | ] 107 | } 108 | ``` 109 | -------------------------------------------------------------------------------- /resources/users/blocking.md: -------------------------------------------------------------------------------- 1 | # User Blocking 2 | 3 | Endpoints: 4 | 5 | * [Get blocked users](#get-users-id-blocked) 6 | * [Block a user](#put-users-id-block) 7 | * [Unblock a user](#delete-users-id-block) 8 | 9 | Blocking a user prevents the blocked user and the blocking user from seeing each other on the network except in a few necessary places. 10 | 11 | 12 | ## GET /users/{user_id}/blocked {#get-users-id-blocked .endpoint} 13 | 14 | Scope: any 15 | 16 | Retrieve a list of blocked users. Users may only see their own list of blocked users. 17 | 18 | ### URL Parameters 19 | 20 | Name|Description 21 | -|- 22 | `user_id`|ID of the user whose list of blocked users to retrieve 23 | 24 | ##### Example {.example-code} 25 | 26 | ```bash 27 | curl "https://api.pnut.io/v1/users/me/blocked" \ 28 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 29 | -H "X-Pretty-Json: 1" 30 | ``` 31 | 32 | Returns a list of users 33 | 34 | ```json 35 | { 36 | "meta": { 37 | "more": false, 38 | "code": 200 39 | }, 40 | "data": [ 41 | {"...User Object..."} 42 | ] 43 | } 44 | ``` 45 | 46 | 47 | ## PUT /users/{user_id}/block {#put-users-id-block .endpoint} 48 | 49 | Token: user 50 | 51 | Scope: follow 52 | 53 | Block a user. Blocked users will not show up in future requests, the same as if they were muted. Blocked users also cannot retrieve this authorized user in their requests. A user can block another user even if the other user is blocking them (but will only return an ID of the blocked user). 54 | 55 | ### URL Parameters 56 | 57 | Name|Description 58 | -|- 59 | `user_id`|ID of the user to block 60 | 61 | ##### Example {.example-code} 62 | 63 | ```bash 64 | curl "https://api.pnut.io/v1/users/@testuser/block" \ 65 | -X PUT \ 66 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 67 | -H "X-Pretty-Json: 1" 68 | ``` 69 | 70 | Returns the blocked user 71 | 72 | ```json 73 | { 74 | "meta": { 75 | "code": 200 76 | }, 77 | "data": {"....User Object..."} 78 | } 79 | ``` 80 | 81 | 82 | ## DELETE /users/{user_id}/block {#delete-users-id-block .endpoint} 83 | 84 | Token: user 85 | 86 | Scope: follow 87 | 88 | Unblock a user. A user can unblock another user even if the other user is blocking them (but will only return an ID of the unblocked user). 89 | 90 | ### URL Parameters 91 | 92 | Name|Description 93 | -|- 94 | `user_id`|ID of the user to unblock 95 | 96 | ##### Example {.example-code} 97 | 98 | ```bash 99 | curl "https://api.pnut.io/v1/users/@testuser/block" \ 100 | -X DELETE \ 101 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 102 | -H "X-Pretty-Json: 1" 103 | ``` 104 | 105 | Returns the unblocked user 106 | 107 | ```json 108 | { 109 | "meta": { 110 | "code": 200 111 | }, 112 | "data": {"...User Object...."} 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /resources/users/following.md: -------------------------------------------------------------------------------- 1 | # User Following 2 | 3 | Endpoints: 4 | 5 | * [Get followed users](#get-users-id-following) 6 | * [Get a user's followers](#get-users-id-followers) 7 | * [Follow a user](#put-users-id-follow) 8 | * [Unfollow a user](#delete-users-id-follow) 9 | 10 | 11 | ## GET /users/{user_id}/following {#get-users-id-following .endpoint} 12 | 13 | Scope: any 14 | 15 | Retrieve a list of user objects that the specified user is following. 16 | 17 | ### URL Parameters {#url-parameters-1} 18 | 19 | Name|Description 20 | -|- 21 | `user_id`|ID of the user whose following to retrieve 22 | 23 | ### Query Parameters 24 | 25 | #### Sort 26 | 27 | Name|Description 28 | -|- 29 | `order`|One of id, last_post_id, or followed_at. Default is by followed_at 30 | 31 | ##### Example {.example-code} 32 | 33 | ```bash 34 | curl "https://api.pnut.io/v1/users/3/following?count=2" \ 35 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 36 | -H "X-Pretty-Json: 1" 37 | ``` 38 | 39 | Returns a list of users 40 | 41 | ```json 42 | { 43 | "meta": { 44 | "more": false, 45 | "max_id": "0", 46 | "min_id": "0", 47 | "code": 200 48 | }, 49 | "data": [ 50 | {"...User Object..."} 51 | ] 52 | } 53 | ``` 54 | 55 | 56 | ## GET /users/{user_id}/followers {#get-users-id-followers .endpoint} 57 | 58 | Scope: any 59 | 60 | Retrieve a list of user objects that are following the specified user. 61 | 62 | ### URL Parameters {#url-parameters-2} 63 | 64 | Name|Description 65 | -|- 66 | `user_id`|ID of the user whose followers to retrieve 67 | 68 | ### Query Parameters 69 | 70 | #### Sort 71 | 72 | Name|Description 73 | -|- 74 | `order`|One of id, last_post_id, or followed_at. Default is by followed_at 75 | 76 | 77 | ##### Example {.example-code} 78 | 79 | ```bash 80 | curl "https://api.pnut.io/v1/users/@shawn/followers?count=2" \ 81 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 82 | -H "X-Pretty-Json: 1" 83 | ``` 84 | 85 | Returns a list of users 86 | 87 | ```json 88 | { 89 | "meta": { 90 | "more": false, 91 | "max_id": "0", 92 | "min_id": "0", 93 | "code": 200 94 | }, 95 | "data": [ 96 | {"...User Object..."}, 97 | {"...User Object..."} 98 | ] 99 | } 100 | ``` 101 | 102 | 103 | ## PUT /users/{user_id}/follow {#put-users-id-follow .endpoint} 104 | 105 | Token: user 106 | 107 | Scope: follow 108 | 109 | Follow a user. 110 | 111 | ### URL Parameters {#url-parameters-3} 112 | 113 | Name|Description 114 | -|- 115 | `user_id`|ID of the user to follow 116 | 117 | 118 | ##### Example {.example-code} 119 | 120 | ```bash 121 | curl "https://api.pnut.io/v1/users/246/follow" \ 122 | -X PUT \ 123 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 124 | -H "X-Pretty-Json: 1" 125 | ``` 126 | 127 | Returns the followed user 128 | 129 | ```json 130 | { 131 | "meta": { 132 | "code": 200 133 | }, 134 | "data": {"...User Object..."} 135 | } 136 | ``` 137 | 138 | 139 | ## DELETE /users/{user_id}/follow {#delete-users-id-follow .endpoint} 140 | 141 | Token: user 142 | 143 | Scope: follow 144 | 145 | Unfollow a user. 146 | 147 | ### URL Parameters {#url-parameters-4} 148 | 149 | Name|Description 150 | -|- 151 | `user_id`|ID of the user to unfollow 152 | 153 | ##### Example {.example-code} 154 | 155 | ```bash 156 | curl "https://api.pnut.io/v1/users/246/follow" \ 157 | -X DELETE \ 158 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 159 | -H "X-Pretty-Json: 1" 160 | ``` 161 | 162 | Returns the unfollowed user 163 | 164 | ```json 165 | { 166 | "meta": { 167 | "code": 200 168 | }, 169 | "data": {"....User Object..."} 170 | } 171 | ``` -------------------------------------------------------------------------------- /resources/users/interactions.md: -------------------------------------------------------------------------------- 1 | # User Interactions 2 | 3 | Endpoints: 4 | 5 | * [Get user interactions](#get-users-me-interactions) 6 | 7 | Users may see some common actions that have been made against their self, posts, polls, etcetera. Events from multiple users are grouped into the most recent event of the same type and object, within a short time. 8 | 9 | *E.g.*, if two users repost the same post of yours within a day, they may be included in a single event. But further apart, they would be included as separate events. 10 | 11 | ### Objects by Action 12 | 13 | Action|Objects 14 | -|- 15 | `bookmark`, `reply`, `repost`|Post 16 | `follow`|User 17 | `poll_response`|Poll 18 | 19 | The poll included from a `poll_response` action is static and abbreviated like it is when included embedded in `io.pnut.core.poll` raw. If the poll is anonymous, `users` will not be included. 20 | 21 | 22 | ## GET /users/me/interactions {#get-users-me-interactions .endpoint} 23 | 24 | Scope: any 25 | 26 | Retrieve actions executed against the authenticated user and their content. 27 | 28 | ### Query Parameters 29 | 30 | Name|Description 31 | -|- 32 | `filters`|Comma-separated list of actions to filter by. `?filters=bookmark` will only include bookmarks. Allowed: `bookmark`, `repost`, `reply`, `follow`, `poll_response`. 33 | `exclude`|Comma-separated list of actions to exclude. `?exclude=bookmark` will return all actions except bookmarks. If `filters` is also specified, this is ignored. 34 | 35 | ##### Example {.example-code} 36 | 37 | ```bash 38 | curl "https://api.pnut.io/v1/users/me/interactions" \ 39 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 40 | -H "X-Pretty-Json: 1" 41 | ``` 42 | 43 | Returns a list of interactions. 44 | 45 | ```json 46 | { 47 | "meta": { 48 | "more": true, 49 | "max_id": "0", 50 | "min_id": "0", 51 | "code": 200 52 | }, 53 | "data": [ 54 | { 55 | "pagination_id": "0", 56 | "event_date": "ISO 8601", 57 | "action": "String", 58 | "objects": [ 59 | {"...Post Object..."} 60 | ], 61 | "users": [ 62 | {"...User Object..."}, 63 | {"...User Object..."} 64 | ] 65 | } 66 | ] 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /resources/users/lookup.md: -------------------------------------------------------------------------------- 1 | # User Lookup 2 | 3 | Endpoints: 4 | 5 | * [Get a user](#get-users-id) 6 | * [Get multiple users](#get-users) 7 | * [Get app user IDs](#get-apps-me-users-ids) 8 | * [Get app user tokens](#get-apps-me-users-tokens) 9 | 10 | 11 | ## GET /users/{user_id} {#get-users-id .endpoint} 12 | 13 | Scope: none 14 | 15 | Retrieve a user object. 16 | 17 | ### URL Parameters 18 | 19 | Name|Description 20 | -|- 21 | `user_id`|User ID or username with "@" symbol of the user to retrieve 22 | 23 | ##### Example {.example-code} 24 | 25 | ```bash 26 | curl "https://api.pnut.io/v1/users/1" \ 27 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 28 | -H "X-Pretty-Json: 1" 29 | ``` 30 | 31 | Returns the user object 32 | 33 | ```json 34 | { 35 | "meta": { 36 | "code": 200 37 | }, 38 | "data": { 39 | "...User Object..." 40 | } 41 | } 42 | ``` 43 | 44 | 45 | ## GET /users {#get-users .endpoint} 46 | 47 | Scope: none 48 | 49 | Retrieve a list of specified user objects. Only retrieves the first 200 found. 50 | 51 | ### Query String Parameters 52 | 53 | Name|Description 54 | -|- 55 | `ids`|Comma-separated list of user IDs or usernames with "@" symbol 56 | 57 | ##### Example {.example-code} 58 | 59 | ```bash 60 | curl "https://api.pnut.io/v1/users?ids=4,@ludolphus,1000" \ 61 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 62 | -H "X-Pretty-Json: 1" 63 | ``` 64 | 65 | Returns a list of users 66 | 67 | ```json 68 | { 69 | "meta": { 70 | "code": 200 71 | }, 72 | "data": [ 73 | {"...User Object..."}, 74 | {"...User Object..."} 75 | ] 76 | } 77 | ``` 78 | 79 | 80 | ## GET /apps/me/users/ids {#get-apps-me-users-ids .endpoint} 81 | 82 | Token: app 83 | 84 | Retrieve a list of all user IDs that authorize the requesting app. It is not paginated. 85 | 86 | Requires an app token. 87 | 88 | ##### Example {.example-code} 89 | 90 | ```bash 91 | curl "https://api.pnut.io/v1/apps/me/users/ids" \ 92 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 93 | -H "X-Pretty-Json: 1" 94 | ``` 95 | 96 | Returns a list of user IDs 97 | 98 | ```json 99 | { 100 | "meta": { 101 | "code": 200 102 | }, 103 | "data": [ 104 | "0", 105 | "0", 106 | "0" 107 | ] 108 | } 109 | ``` 110 | 111 | 112 | ## GET /apps/me/users/tokens {#get-apps-me-users-tokens .endpoint} 113 | 114 | Token: app 115 | 116 | Retrieve a list of all user token objects that authorize the requesting app. Not currently paginated. 117 | 118 | Requires an app token. 119 | 120 | ##### Example {.example-code} 121 | 122 | ```bash 123 | curl "https://api.pnut.io/v1/apps/me/users/tokens" \ 124 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 125 | -H "X-Pretty-Json: 1" 126 | ``` 127 | 128 | Returns a list of user tokens 129 | 130 | ```json 131 | { 132 | "meta": { 133 | "code": 200 134 | }, 135 | "data": [ 136 | {"..User Token..."}, 137 | {"..User Token..."}, 138 | {"..User Token..."} 139 | ] 140 | } 141 | ``` -------------------------------------------------------------------------------- /resources/users/muting.md: -------------------------------------------------------------------------------- 1 | # User Muting 2 | 3 | Endpoints: 4 | 5 | * [Get muted users](#get-users-id-muted) 6 | * [Mute a user](#put-users-id-mute) 7 | * [Unmute a user](#delete-users-id-mute) 8 | 9 | Muting a user will prevent them from being visible to the muting user unless specifically requested. 10 | 11 | 12 | ## GET /users/{user_id}/muted {#get-users-id-muted .endpoint} 13 | 14 | Scope: any 15 | 16 | Retrieve a list of muted users. Users may only see their own list of muted users. 17 | 18 | ### URL Parameters 19 | 20 | Name|Description 21 | -|- 22 | `user_id`|ID of the user whose list of muted users to retrieve 23 | 24 | ##### Example {.example-code} 25 | 26 | ```bash 27 | curl "https://api.pnut.io/v1/users/me/muted" \ 28 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 29 | -H "X-Pretty-Json: 1" 30 | ``` 31 | 32 | Returns a list of users 33 | 34 | ```json 35 | { 36 | "meta": { 37 | "more": false, 38 | "code": 200 39 | }, 40 | "data": [ 41 | {"...User Object..."} 42 | ] 43 | } 44 | ``` 45 | 46 | 47 | ## PUT /users/{user_id}/mute {#put-users-id-mute .endpoint} 48 | 49 | Token: user 50 | 51 | Scope: follow 52 | 53 | Mute a user. Muted users will not appear in future requests. 54 | 55 | ### URL Parameters 56 | 57 | Name|Description 58 | -|- 59 | `user_id`|ID of the user to mute 60 | 61 | ##### Example {.example-code} 62 | 63 | ```bash 64 | curl "https://api.pnut.io/v1/users/@testuser/mute" \ 65 | -X PUT \ 66 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 67 | -H "X-Pretty-Json: 1" 68 | ``` 69 | 70 | Returns the muted user 71 | 72 | ```json 73 | { 74 | "meta": { 75 | "code": 200 76 | }, 77 | "data": {"...User Object..."} 78 | } 79 | ``` 80 | 81 | 82 | ## DELETE /users/{user_id}/mute {#delete-users-id-mute .endpoint} 83 | 84 | Token: user 85 | 86 | Scope: follow 87 | 88 | Unmute a user. 89 | 90 | ### URL Parameters 91 | 92 | Name|Description 93 | -|- 94 | `user_id`|ID of the user to unmute 95 | 96 | ##### Example {.example-code} 97 | 98 | ```bash 99 | curl "https://api.pnut.io/v1/users/@testuser/mute" \ 100 | -X DELETE \ 101 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 102 | -H "X-Pretty-Json: 1" 103 | ``` 104 | 105 | Returns the unmuted user 106 | 107 | ```json 108 | { 109 | "meta": { 110 | "code": 200 111 | }, 112 | "data": {"....User Object..."} 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /resources/users/presence.md: -------------------------------------------------------------------------------- 1 | # User Presence 2 | 3 | Endpoints: 4 | 5 | * [Get present users](#get-presence) 6 | * [Get a user's presence](#get-users-id-presence) 7 | * [Update the authenticated user's presence](#put-users-me-presence) 8 | 9 | A user's presence is the recent status of that user. Each updated presence lasts for 15 minutes by default, or until another status is given, up to seven days. Users who do not have a current status show up as "offline". 10 | 11 | __Update from any Endpoint Call__ 12 | 13 | A user's status can be updated on *any* authenticated call similarly to the [update presence call](#put-users-me-presence): 14 | 15 | * Include the `update_presence` query parameter with a status for its value. 16 | * An optional `update_presence_expiration` parameter can be included to specify the expiration. 17 | 18 | If this method is attempted, the response's `meta.updated_presence` key will be set and `true`. If it fails to update, it will be `false`. 19 | 20 | ### Return `presence` Object Parameters 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
FieldTypeDescription
avatar_imagestringURL linking to the current avatar image.
idstringPrimary identifier for the user that made the update.
last_seen_atstringOptional time at which the presence was updated, in ISO 8601 format; YYYY-MM-DDTHH:MM:SSZ.
namestringOptional user-supplied name. All Unicode characters allowed. Maximum length 50 characters. Be sure to escape if necessary.
presencestringUpdated user presence, up to 100 characters.
usernamestringUsername of the user that made the update. Case sensitive. 20 characters, may only contain a-z, 0-9 and underscore.
59 | 60 | 61 | ## GET /presence {#get-presence .endpoint} 62 | 63 | Token: user 64 | 65 | Scope: any 66 | 67 | Retrieve all users' presence statuses that are not "offline". 68 | 69 | ##### Example {.example-code} 70 | 71 | ```bash 72 | curl "https://api.pnut.io/v1/presence" \ 73 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 74 | -H "X-Pretty-Json: 1" 75 | ``` 76 | 77 | Returns a list of users' presences. 78 | 79 | ```json 80 | { 81 | "meta": { 82 | "code": 200 83 | }, 84 | "data": [] 85 | } 86 | ``` 87 | 88 | 89 | ## GET /users/{user_id}/presence {#get-users-id-presence .endpoint} 90 | 91 | Token: user 92 | 93 | Scope: any 94 | 95 | Retrieve a user's presence. 96 | 97 | If the user has never set a presence, `last_seen_at` will not be set. 98 | 99 | ### URL Parameters 100 | 101 | Name|Description 102 | -|- 103 | `user_id`|ID of the user to retrieve 104 | 105 | ##### Example {.example-code} 106 | 107 | ```bash 108 | curl "https://api.pnut.io/v1/users/1/presence" \ 109 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 110 | -H "X-Pretty-Json: 1" 111 | ``` 112 | 113 | Returns a user's current presence. 114 | 115 | ```json 116 | { 117 | "meta": { 118 | "code": 200 119 | }, 120 | "data": { 121 | "avatar_image": "String", 122 | "id": "0", 123 | "last_seen_at": "ISO 8601", 124 | "name": "String", 125 | "presence": "String", 126 | "username": "String" 127 | } 128 | } 129 | ``` 130 | 131 | 132 | ## PUT /users/me/presence {#put-users-me-presence .endpoint} 133 | 134 | Token: user 135 | 136 | Scope: presence 137 | 138 | Update a user's presence. 139 | 140 | If the `update_presence` query parameter is set on this call, it will override this call. It will not occur twice. 141 | 142 | ### PUT Parameters 143 | 144 | Name|Description 145 | -|- 146 | `presence`|A string up to 100 unicode characters. If not set, or if it is set to `1`, it will be updated to `"online"`. A value of `"offline"` or `0` will delete the user's presence and remove them from the [list of users online](#get-presence). 147 | `expiration`|Unix timestamp for when the given presence should expire. Default is 15 minutes into the future. If set too far into the future, will set to the maximum of 7 days. If set to a past timestamp, will delete the user's presence. 148 | 149 | ##### Example {.example-code} 150 | 151 | ```bash 152 | curl "https://api.pnut.io/v1/users/me/presence" \ 153 | -X PUT \ 154 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 155 | -d "presence=keeping my stick on the ice&expiration=1721702296" \ 156 | -H "X-Pretty-Json: 1" 157 | ``` 158 | 159 | Returns the updated user presence. 160 | 161 | ```json 162 | { 163 | "meta": { 164 | "code": 200 165 | }, 166 | "data": { 167 | "avatar_image": "String", 168 | "id": "0", 169 | "last_seen_at": "ISO 8601", 170 | "name": "String", 171 | "presence": "String", 172 | "username": "String" 173 | } 174 | } 175 | ``` 176 | -------------------------------------------------------------------------------- /resources/users/search.md: -------------------------------------------------------------------------------- 1 | # User Search 2 | 3 | Endpoints: 4 | 5 | * [Search users](#get-users-search) 6 | * [Suggested users](#get-users-suggested) 7 | 8 | 9 | ## GET /users/search {#get-users-search .endpoint} 10 | 11 | Scope: none 12 | 13 | Retrieve a list of users filtered by the given criteria. 14 | 15 | ### Query Parameters 16 | 17 | #### Search 18 | 19 | Name|Description 20 | -|- 21 | `q`|List of words included in user profiles or names 22 | 23 | #### Sort 24 | 25 | Name|Description 26 | -|- 27 | `order`|One of id or relevance. Default is by relevance when a `q` value is set 28 | 29 | #### Filter 30 | 31 | Name|Description 32 | -|- 33 | `created_after`|ISO 8601-formatted timestamp after which users were created 34 | `created_before`|ISO 8601-formatted timestamp before which users were created 35 | `is_follower`|Whether to include users who follow you. Requires authentication 36 | `is_following`|Whether to include users who you are following. Requires authentication 37 | `locale`|[Valid user locale](../users#locales) e.g., `en_US` 38 | `timezone`|[Valid user timezone](../users#timezones) e.g., `America/Chicago` 39 | `user_types`|Comma-separated list of user types of: human, feed, bot 40 | 41 | ##### Example {.example-code} 42 | 43 | ```bash 44 | curl "https://api.pnut.io/v1/users/search?q=news&user_types=feed" \ 45 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 46 | -H "X-Pretty-Json: 1" 47 | ``` 48 | 49 | Returns a list of users 50 | 51 | ```json 52 | { 53 | "meta": { 54 | "more": false, 55 | "max_id": "0", 56 | "min_id": "0", 57 | "code": 200 58 | }, 59 | "data": [ 60 | {"...User Object..."}, 61 | {"...User Object..."} 62 | ] 63 | } 64 | ``` 65 | 66 | 67 | 68 | ## GET /users/suggested {#get-users-suggested .endpoint} 69 | 70 | Token: user 71 | 72 | Scope: follow 73 | 74 | Get a list of suggested users to follow. Effectively: 75 | 76 | - followed by users you follow (if you are following some users) 77 | - actively posts 78 | - not blocked or muted 79 | 80 | ##### Example {.example-code} 81 | 82 | ```bash 83 | curl "https://api.pnut.io/v1/users/suggested" \ 84 | -H "Authorization: Bearer ${ACCESS_TOKEN}" \ 85 | -H "X-Pretty-Json: 1" 86 | ``` 87 | 88 | Returns a list of users 89 | 90 | ```json 91 | { 92 | "meta": { 93 | "more": false, 94 | "max_id": "0", 95 | "min_id": "0", 96 | "code": 200 97 | }, 98 | "data": [ 99 | {"...User Object..."}, 100 | {"...User Object..."} 101 | ] 102 | } 103 | ``` 104 | --------------------------------------------------------------------------------