TheSentinelBot (TSB) is permanently shutting down. Unfortunately we are no longer able to maintain the TSB project mainly due to Google restricting our API usage and also due to a lack of developer and system resources. We've submitted requests to Google for raising the rate limit on our API key, however those have been denied. As such, we will soon be shutting down all our TSB bots indefinitely. If you'd like to export any of your data, please reach out to us in our Discord. Thank you for using TSB up until now.

Developers //

create an app  |  api documentation

API Reference

This page is under construction.

Welcome to the Layer7 Solutions API reference. We provide an API for you to use for your own subreddit-specific applications or anything else you wish. Using our API you can manage your TheSentinelBot blacklist, fetch mod matrices, mod logs, and more.

Have a question or need help? Try asking in our subreddit at /r/Layer7

Overview

The web API takes the form https://layer7.solutions/api/v1/METHOD.
For example, https://layer7.solutions/api/v1/identity.

All methods must be called using HTTPS. The response will always be in JSON format. The properties within the response are defined in the documentation for the relevant method.

Errors

If an error occurred, there will be two properties: error and error_description. The error code will remain constant for the given error type, whereas the error_description is a human-readable description of the error and may change at any time.

        {
            "error": "ERROR_CODE",
            "error_description": "Something happened",
        }
        

The HTTP status code will also be different from the usual 200. For example, if your request had invalid or missing parameters then the status code will be 400 Bad request, and if you attempt to make a request on a subreddit you don't mod then it'll be 403 Forbidden.

Authenticating

The api uses OAuth 2. If you are unsure of how to use OAuth 2, search for OAuth 2 tutorials.

Our OAuth2 server currently only enables these grant types: client_credentials, authorization_code, refresh_token

OAuth URLs
Authorize URL https://layer7.solutions/api/v1/authorize
Access Token URL https://layer7.solutions/api/v1/access_token

To register an application click here.

API Usage Rules

Overall, our API rules aren't too strict. But if there is precedent we may make them more strict overtime. Any changes to this API will be announced on our subreddit.

  • Authentication API clients must authenticate using OAuth 2
  • Rate Limit: currently we do not have a rate limit, this is subject to change if it becomes a problem. So please use our API responsibly. You shouldn't really be going over 60 requests per minute.
  • User-Agent: the client's user-agent should be something unique and descriptive that identifies your application.
    • Do not lie about your user-agent. Such as masquerading as other bots or applications.
  • Do not use our API for malicious purposes. We do reserve the right to refuse service to any user or subreddit mod team.

Quick guide on how to integrate with your PRAW bot or other Python applications

With Python 3, requests library, and the client_credentials grant type.
Or you could use a OAuth2 python library.


First click this link and register your OAuth2 application if you haven't done so already. Take note of the generated Consumer ID and Consumer Secret.

Then request an access token:

        >>> import requests
        >>> import json

        >>> r = requests.post('https://layer7.solutions/api/v1/access_token',
                data = {
                    'grant_type': 'client_credentials',
                    'scope': 'identity tsbread' # space-delimited scope list
                },
                auth = (
                    'e0aaV-pQQ_vPpu', # Consumer ID
                    'f4fJPcr6UbpnBolBn_TI4HZZPKwQkYLvtMgvFiZ7oDEA0n5kDs' # Consumer Secret
                ))
        >>> r.status_code
        200
        >>> print(json.dumps(r.json(), indent=2))
        {
          'access_token': 'fc92463c2a1753ed1be23c84b0fd42496bbaf45f',
          'expires_in': 3600,
          'token_type': 'Bearer',
          'scope': 'identity tsbread'
        }
        

And then add the Authorization: Bearer <ACCESS TOKEN HERE> header to your requests:

        >>> r2 = requests.get('https://layer7.solutions/api/v1/videos/blacklist/reports',
                params = {
                    'type': 'channel_name',
                    'subject': 'funny videos',
                    'amount': 5
                },
                headers = {
                    'Authorization': r.json()['token_type'] + ' ' + r.json()['access_token']
                })
        >>> r2.status_code
        200
        >>> print(json.dumps(r2.json(), indent=2))
        {
          "matter": [
            {
              "subject": "funny videos",
              "type": "channel_name",
              "matter_id": "channel_name;funny videos"
            }
          ],
          "listing": [
            {
              "id": 64925062,
              "removed": false,
              "action_utc": 1513598213.2133,
              "subreddit": "videos",
              "author": "ngochaivp91",
              "thing_id": "t3_7kkrmd",
              "thing_title": "Funny videos 2017!Secret of doing stupid actions - HD sex Dino 2017 #3",
              "thing_data": "",
              "thing_created": 1513598163,
              "permalink": "http://reddit.com/7kkrmd",
              "media_author": "Funny Videos",
              "media_channel_id": "UCh6EkQQCkxOX_aEIJs0Ftew",
              "media_link": "https://www.youtube.com/attribution_link?a=vQhiRShsXZc&u=%2Fwatch%3Fv%3DvdnT4kqmP0s%26feature%3Dshare",
              "media_platform_id": 1,
              "media_channel_url": null,
              "media_platform": "YouTube",
            },
            {
              "id": 64526546,
              "removed": false,
              "action_utc": 1513412690.6086,
              "subreddit": "videos",
              "author": "Isadoscomedy",
              "thing_id": "t1_drbtfws",
              "thing_title": null,
              "thing_data": "Most funniest video https://youtu.be/PCnw7y_7x2k",
              "thing_created": 1513412664,
              "permalink": "http://reddit.com/comments/7k3eeu/-/drbtfws",
              "media_author": "Most Funny Videos Latest",
              "media_channel_id": "UCYytxrvf1C5TIN0Macafw4g",
              "media_link": "https://youtu.be/PCnw7y_7x2k",
              "media_platform_id": 1,
              "media_channel_url": null,
              "media_platform": "YouTube",
            },
            {
              "id": 64525973,
              "removed": false,
              "action_utc": 1513412324.8388,
              "subreddit": "videos",
              "author": "Isadoscomedy",
              "thing_id": "t1_drbtb1e",
              "thing_title": null,
              "thing_data": "Are you interested in laughing out your sorrow? Then click here https://youtu.be/S6e1XJcRWVM",
              "thing_created": 1513412308,
              "permalink": "http://reddit.com/comments/7k1qia/-/drbtb1e",
              "media_author": "Most Funny Videos Latest",
              "media_channel_id": "UCYytxrvf1C5TIN0Macafw4g",
              "media_link": "https://youtu.be/S6e1XJcRWVM",
              "media_platform_id": 1,
              "media_channel_url": null,
              "media_platform": "YouTube",
            },
            {
              "id": 64525902,
              "removed": false,
              "action_utc": 1513412272.9849,
              "subreddit": "videos",
              "author": "Isadoscomedy",
              "thing_id": "t1_drbta4n",
              "thing_title": null,
              "thing_data": "Are you interested in laughing out your sorrow? Then click here https://youtu.be/S6e1XJcRWVM",
              "thing_created": 1513412241,
              "permalink": "http://reddit.com/comments/7k3wwc/-/drbta4n",
              "media_author": "Most Funny Videos Latest",
              "media_channel_id": "UCYytxrvf1C5TIN0Macafw4g",
              "media_link": "https://youtu.be/S6e1XJcRWVM",
              "media_platform_id": 1,
              "media_channel_url": null,
              "media_platform": "YouTube",
            },
            {
              "id": 64525799,
              "removed": false,
              "action_utc": 1513412193.3,
              "subreddit": "videos",
              "author": "Isadoscomedy",
              "thing_id": "t1_drbt981",
              "thing_title": null,
              "thing_data": "Are you interested in laughing out your sorrow? Then click here https://youtu.be/S6e1XJcRWVM",
              "thing_created": 1513412174,
              "permalink": "http://reddit.com/comments/7k28dr/-/drbt981",
              "media_author": "Most Funny Videos Latest",
              "media_channel_id": "UCYytxrvf1C5TIN0Macafw4g",
              "media_link": "https://youtu.be/S6e1XJcRWVM",
              "media_platform_id": 1,
              "media_channel_url": null,
              "media_platform": "YouTube",
            }
          ],
          "amount": 5,
          "offset": 0,
          "total": 1297
        }
        >>>
        

And that's it! And don't forget about the 'expires_in': 3600 part of the access_token response. You'll have to fetch a new access token every hour.

Here's a list of all our scopes for ease of copying:

Subreddit Notation

Take note that wherever you see {subreddit}, whether as part of the path or as a query parameter value, you can use our Subreddit Notation

However, wherever you see {srname} allows only one subreddit.

API Methods // identity

GET:/identity

Returns the current user's username, a boolean value representing whether the user mods at least one subreddit, and some time information about the current user's activity on the layer7.solutions website.

Arguments

None

Example Response

            {
                "username": "kwwxis",
                "first_login": 1477958400,
                "last_login": 1510016101,
                "last_access": 1513878367
            }
            

GET:/identity/subreddits[/{category}]

Returns the subreddits the current user mods in no particular order.

Arguments

category (optional)

Returns subreddits the current users mods that are in the given category. Raises UNKNOWN_FIELD error if unknown category.

detail (optional)

If true, returns subreddit name to detail map rather than an array of just subreddit names.

Example Response

            [
                "pokemon",
                "CrucibleSherpa",
                "DestinySherpa",
                "worldnews",
                "LifeProTips",
                "aww",
                ...
            ]
            

GET:/identity/websync_history

Gives a descending history of the current user's moderator history on subreddits that have TSB.

Arguments

limit (optional)

Limit results to a specific number. If this parameter is not present then the entire history will be returned.

Example Response

The moderator property will always be the username of the current user. The moderator who performed the action on the current user is not available for this api method.

            [
                {
                    "id": 6120,
                    "type": "removemoderator",
                    "subreddit": "Scholar",
                    "moderator": "kwwxis",
                    "new_state": null,
                    "history_utc": 1512520179
                },
                {
                    "id": 5009,
                    "type": "setpermissions",
                    "subreddit": "Fireteams",
                    "moderator": "kwwxis",
                    "new_state": "config,flair",
                    "history_utc": 1506961057
                },
                {
                    "id": 4991,
                    "type": "setpermissions",
                    "subreddit": "DestinySherpa",
                    "moderator": "kwwxis",
                    "new_state": "config,flair",
                    "history_utc": 1506904167
                },
                {
                    "id": 4990,
                    "type": "setpermissions",
                    "subreddit": "CrucibleSherpa",
                    "moderator": "kwwxis",
                    "new_state": "config,flair",
                    "history_utc": 1506904108
                },
                {
                    "id": 4811,
                    "type": "acceptmoderatorinvite",
                    "subreddit": "aww",
                    "moderator": "kwwxis",
                    "new_state": null,
                    "history_utc": 1506199854
                },
                {
                    "id": 4809,
                    "type": "removemoderator",
                    "subreddit": "aww",
                    "moderator": "kwwxis",
                    "new_state": null,
                    "history_utc": 1506199854
                },
                ...
            ]
            

API Methods // srprefread or srprefedit

GET:/r/{srname}/settings

Returns the current subreddit settings.

Arguments

action (optional)

  • get: get the value for a specific property
  • set: change a property's value. Requires srprefedit scope.
  • Not given: fetch the entire subreddit settings

property (optional)

The property that is the subject of the request. Required only if the action parameter is set.

Raises UNKNOWN_FIELD error if unknown property

value (optional)

The new value. Required only if action is set. Must be one of these values if to be interpreted as true: t, true, 1, yes. All other values will be interpreted as false.

Example Responses

No parameters:

            {
                "id": 694,
                "subreddit": "aww",
                "redditbot_name": "TheSentinel_8",
                "redditbot_permissions": "posts,flair",
                "sentinel_enabled": true,
                "modlog_enabled": true,
                "modmail_enabled": false,
                "botban_enabled": true,
                "domainblacklist_enabled": true,
                "dirtbag_enabled": true
            }
            

?action=get&property=sentinel_enabled:

            {
                "property_key": "sentinel_enabled",
                "property_value": true
            }
            

?action=set&property=modmail_enabled&value=f:

            {
                "property_key": "modmail_enabled",
                "property_new_value": false,
                "property_old_value": true
            }
            

API Methods // logsread

GET:/logs/view

Fetches a page from the user's subreddit moderation logs.

Arguments

query (required)

The filter query as a stringified JSON string. To grab a page with no filter, set this value to empty string or {}.

The format of this JSON string follows:

                {
                    "tokens": [ // tokens (optional)
                        {
                            "label": "somelabel", // token label (required)
                            "modifier": null, // token modifier (optional, default:null)
                            "values": [ // token values (at least 1 value is required)
                                "somevalue"
                            ]
                        }
                    ]
                }
                

The query in the following example will return log entries for sticky and removelink actions in /r/DestinyTheGame and /r/videos.

                {
                "tokens": [
                    {
                        "label": "in",
                        "values": [
                            "destinythegame",
                            "videos"
                        ]
                    },
                    {
                        "label": "action",
                        "values":[
                            "removelink",
                            "sticky"
                        ]
                    }
                ]
                }
                

Here is a list of valid labels and modifiers:

  • label: in

    Multiple values: allowed

    Use this token label to limit your query to specific subreddit(s)

  • label: after

    Multiple values: no

    Use this label to limit your query to actions preformed after this timestamp. You can use this in conjunction with before if you want a specific range.

  • label: before

    Multiple values: no

    Use this label to limit your query to actions preformed before this timestamp. You can use this in conjunction with after if you want a specific range.

  • label: date

    Multiple values: no

    Use this label to limit your query to actions between the given timestamp and timestamp + 86400

  • label: mod

    Multiple values: allowed

    Use this label to limit your query to a specific set of moderator(s). Do not include the "/u/", just the moderator username.

  • label: action

    Multiple values: allowed

    Use this label to limit your query to a specific set of mod action types. See this page for a list of valid action types

  • label: reason

    Multiple values: no

    Search for mod actions whose reason includes the given string in it's reason. The modifier used if no modifier specified is includes

    • mod: includes-word

      Search for reasons containing the given word.

    • mod: includes

      Search for reasons containing the given string.

    • mod: starts-with

      Search for reasons starting with the given string.

    • mod: ends-with

      Search for reasons ending with the given string.

    • mod: full-exact

      Search for reasons that exactly match the given string.

    • mod: full-text

      Search for reasons that exactly match the given string, ignoring leading and trailing whitespace.

    • mod: regex

      Search for reasons that match the given regex.

    • mod: shorter-than

      Search for reasons whose length is shorter than the given number in characters.

    • mod: longer-than

      Search for reasons whose length is longer than the given number in characters.

    You can only include one modifier per token item. But you can use multiple token items. For example, this query will search for reasons longer than 5 characters that also contain "flair":

                            {
                            "tokens": [
                                {
                                    "label": "reason",
                                    "modifier": "longer-than",
                                    "values": [ "5" ]
                                },
                                {
                                    "label": "reason",
                                    "values": [ "flair" ]
                                }
                            ]
                            }
                            
  • label: author

    Multiple values: allowed

    Limit your query to actions whose subject user is in the given set of authors. Do not include the "/u/", just the subject username.

  • label: thingid

    Multiple values: allowed

    Limit your query to actions whose thing ID is in the given set of thing IDs. The thing id should include the t#_ prefix.

before (optional)

Used for pagination. If this argument is present, fetching will start before the entry with the ID that matches the value of this argument. Not inclusive of the entry with the ID. Use before when going back a page.

after (optional)

Used for pagination. If this argument is present, fetching will start after the entry with the ID that matches the value of this argument. Not inclusive of the entry with the ID. Use after when going forward a page.

If both before and after are present, after will be ignored and before will be used.

amount (optional)

The amount of items per page. Max is 100. Default is 25.

Example Response

            {
                "listing": {
                    {
                        "id": 1517333, // the id to use for before and after
                        "thingid": "t3_5pegdn",
                        "mod": "Destiny_Flair_Bot",
                        "author": "BladedAbyss2551",
                        "action": "removelink",
                        "reason": "remove",
                        "permalink": "/r/DestinyTheGame/comments/5pegdn/wrath_of_the_machine_hard_mode_question/",
                        "timestamp": 1485050543,
                        "human_timing": "3 minutes ago",
                        "subreddit": "DestinyTheGame",
                        "modactionid": "ModAction_d090c95c-e046-11e6-9a0a-0e5f443b9416",
                        "thing_title": "Wrath of The Machine Hard Mode Question..."
                    },
                    {
                        "id": 1517332,
                        "thingid": "t3_5peh16",
                        "mod": "CodeWord_Dirtbag",
                        "author": "SeekonlyJoy",
                        "action": "removelink",
                        "reason": "remove",
                        "permalink": "/r/videos/comments/5peh16/abrahamhicks_avoiding_your_thoughts_esther_hicks/",
                        "timestamp": 1485050489,
                        "human_timing": "4 minutes ago",
                        "subreddit": "videos",
                        "modactionid": "ModAction_b0499a0c-e046-11e6-98fc-0ece270f97e6",
                        "thing_title": "#AbrahamHicks ~ Avoiding your #Thoughts ♥ Esther Hicks #LawofAttraction Best Daily Videos Workshop"
                    },

                    ... skipping to last item ...

                    {
                        "id": 1517267,
                        "thingid": "t3_5pecaj",
                        "mod": "AutoModerator",
                        "author": "turcois",
                        "action": "removelink",
                        "reason": "[C11.1] Political - Bernie",
                        "permalink": "/r/videos/comments/5pecaj/no_i_will_not_yield/",
                        "timestamp": 1485048882,
                        "human_timing": "30 minutes ago",
                        "subreddit": "videos",
                        "modactionid": "ModAction_f2ccc8c6-e042-11e6-8778-0e18eb2eb5c7",
                        "thing_title": "\"No, I will not yield!\""
                    }
                },
                "datainfo": { // contains some metadata about 'listing'
                    "sr_count": { // map of # of occurences for each subreddit in 'listing'
                        "DestinyTheGame": 3,
                        "videos": 22
                    },
                    "amount": 25, // same as 'amount' received
                    "before": null, // the id of the first item in 'listing' (will be null if at first page)
                    "after": 1517267, // the id of the last item in 'listing'
                }
            }
            

The before and after properties in datainfo are not copies of the parameters received by the request, but rather the before and after values you should use to traverse to the previous or next page respectively. If before is null, then that means there is no previous page.

Errors

INVALID_QUERY