Tokens

What is a Token?

Tokens protect access to your Tinybird resources.

Any operations to manage your Tinybird resources via the CLI or REST API require a valid Token with the necessary permissions. Access to the APIs you publish in Tinybird are also protected with Tokens.

Tokens can have different scopes. This means you can limit which operations a specific Token can do. You can create Tokens that are, for example, only able to do admin operations on Tinybird resources, or only have READ permission for a specific Data Source.

What should I use Tokens for?

There are two types of Tokens.

  • When performing operations on your account (like importing data, creating Data Sources, or publishing APIs via the CLI or REST API) you need a Token. For this purpose, use a Static Token.
  • When publishing an API that exposes your data to an application, you need a Token to successfully interact with the API. For this purpose, use a JWT.

Tokens

Tinybird Tokens, also known as "Static Tokens", are permanent and meant to be long-term. They are stored inside Tinybird and don't have an expiration date or time. They are useful for backend-to-backend integrations (where you call Tinybird as another service).

Token scopes

When a Token is created, you have the choice to give it a set of zero or more scopes that define which tables can be accessed by that Token, and which methods can be used to access them.

A READ Token can be augmented with a SQL filter. This allows you to further restrict what data a Token grants access to. Using this, you can implement row-level security on a READ-scoped Token.

Available scopes syntax

ValueDescription
DATASOURCES:CREATEEnables your Token to create and append data to Data Sources.
DATASOURCES:APPEND:datasource_nameAllows your Token to append data to the defined Data Sources.
DATASOURCES:DROP:datasource_nameAllows your Token to delete the specified Data Sources.
DATASOURCES:READ:datasource_nameGives your Token read permissions for the specified Data Sources. Also gives read for the quarantine Data Source.
DATASOURCES:READ:datasource_name:sql_filterGives your Token read permissions for the specified table with the sql_filter applied.
PIPES:CREATEAllows your Token to create new Pipes and manipulate existing ones.
PIPES:DROP:pipe_nameAllows your Token to delete the specified Pipe.
PIPES:READ:pipe_nameGives your Token read permissions for the specified Pipe.
PIPES:READ:pipe_name:sql_filterGives your Token read permissions for the specified Pipe with the sql_filter applied.
TOKENSGives your Token the capacity of managing Tokens.
ADMINAll permissions will be granted. You should not use this Token except in really specific cases. Use it carefully!

When adding the DATASOURCES:READ scope to a Token, it automatically gives read permissions to the quarantine Data Source associated with it.

Applying Tokens with filters to queries that use the FINAL clause is not supported. If you need to apply auth filters to deduplications, use an alternative strategy (see deduplication strategies).

Default Workspace Tokens

All Workspaces are created with a set of basic Tokens that you can add to by creating additional Tokens:

  • admin token for that Workspace (for signing JWTs).
  • admin <your-email> token for that Workspace that belongs specifically to your user account (for using with Tinybird CLI).
  • create datasource token for creating Data Sources in that Workspace.
  • user token for creating new Workspaces or deleting ones where are are an admin (see below).

Some Tokens are created automatically by Tinybird during certain operations like scheduled copies, and these Tokens can be updated.

Your User Token

Your User Token is specific to your user account. It's a permanent Token that enables you to perform operations that are not limited to a single Workspace, such as creating new Workspaces.

You can only obtain your User Token from your Workspace in the Tinybird UI > Tokens > user token.

Create a Token

In the Tinybird UI, navigate to Tokens > Plus (+) icon. Descriptively rename the new Token and update its scopes using the table above as a guide.

JSON Web Tokens (JWTs)

BETA

JWTs are currently in public beta. They are not feature-complete and may change in the future. If you have any feedback or suggestions, contact Tinybird in the Slack community.

JWTs are signed tokens that allow you to securely authorize and share data between your application and Tinybird. If you want to read more about JWTs, check out the JWT.io website.

Importantly, JWTs are not stored in Tinybird. They are created by you, inside your application, and signed with a shared secret between your application and Tinybird. Tinybird validates the signature of the JWT, using the shared secret, to ensure it's authentic.

When to use JWTs

The primary purpose for JWTs is to allow your application to call Tinybird API Endpoints from the frontend without proxying via your backend.

If you are building an application where a frontend component needs data from a Tinybird API Endpoint, you can use JWTs to authorize the request directly from the frontend.

The typical pattern looks like this:

  1. A user starts a session in your application.
  2. The frontend requests a JWT from your backend.
  3. Your backend generates a new JWT, signed with the Tinybird shared secret, and returns to the frontend.
  4. The frontend uses the JWT to call the Tinybird API Endpoints directly.

JWT payload

The payload of a JWT is a JSON object that contains the following fields:

Key Example Value Required Description
workspace_id31048b76-52e8-497b-90a4-0c6a5513920dYesThe UUID of your Tinybird Workspace, found in the Workspace list (locate the Workspace name in the nav bar > use Copy ID button), the Settings section (the three dots), or by using tb workspace current from the CLI.
namefrontend_jwtYesUsed to identify the token in the tinybird.pipe_stats_rt table, useful for analytics, does not need to be unique.
exp123123123123YesThe Unix timestamp (UTC) showing the expiry date & time. Once a token has expired, Tinybird returns a 403 HTTP status code.
scopes[{"type": "PIPES:READ", "resource": "requests_per_day", "fixed_params": {"org_id": "testing"}}]YesUsed to pass data to Tinybird, including the Tinybird scope, resources and fixed parameters.
scopes.typePIPES:READYesThe type of scope, e.g., READ. See JWT scopes for supported scopes.
scopes.resourcet_b9427fe2bcd543d1a8923d18c094e8c1 or top_airlinesYesThe ID or name of the Pipe that the scope applies to, i.e., which API Endpoint the token can access.
scopes.fixed_params{"org_id": "testing"}NoPass arbitrary fixed values to the API Endpoint. These values can be accessed by Pipe templates to supply dynamic values at query time.
limits{"rps": 10}NoYou can limit the number of requests per second (rps) the JWT can perform. Learn more in the JWT rate limit section.

Check out the JWT example below to see what a complete payload looks like.

JWT algorithm

Tinybird always uses H256 as the algorithm for JWTs and does not read the alg field in the JWT header. You can skip the alg field in the header.

JWT scopes

ValueDescription
PIPES:READ:pipe_nameGives your Token read permissions for the specified Pipe

JWT expiration

JWTs can have an expiration time that gives each Token a finite lifespan.

Setting the exp field in the JWT payload is mandatory, and not setting it will result in a 403 HTTP status code from Tinybird when requesting the API Endpoint.

Tinybird validates that a JWT has not expired before allowing access to the API Endpoint.

If a Token has expired, Tinybird returns a 403 HTTP status code.

JWT fixed parameters

Fixed parameters allow you to pass arbitrary values to the API Endpoint. These values can be accessed by Pipe templates to supply dynamic values at query time.

For example, consider the following fixed parameter:

Example fixed parameters
{
  "fixed_params": {
    "org_id": "testing"
  }
}

This passes a parameter called org_id with the value testing to the API Endpoint. You can then use this value in your SQL queries:

Example SQL query
SELECT fieldA, fieldB FROM my_pipe WHERE org_id = '{{ String(org_id) }}'

This is particularly useful when you want to pass dynamic values to an API Endpoint that are set by your backend and must be safe from user tampering. A good example is multi-tenant applications that require row-level security, where you need to filter data based on a user or tenant ID.

The value org_id will always be the one specified in the fixed_params. Even if you specify a new value in the URL when requesting the endpoint, Tinybird will always use the one specified in the JWT.

You can use JWT fixed parameters in combination with Pipe dynamic parameters.

JWT example

For example, take the following payload with all required and optional fields:

Example payload
{
    "workspace_id": "31048b76-52e8-497b-90a4-0c6a5513920d",
    "name": "frontend_jwt",
    "exp": 123123123123,
    "scopes": [
        {
            "type": "PIPES:READ",
            "resource": "requests_per_day",
            "fixed_params": {
                "org_id": "testing"
            }
        }
    ],
    "limits": {
      "rps": 10
    }
}

Then use the Admin Token from your Workspace to sign the payload, for example:

Example Workspace Admin Token
p.eyJ1IjogIjA1ZDhiYmI0LTdlYjctNDAzZS05NGEyLWM0MzFhNDBkMWFjZSIsICJpZCI6ICI3NzUxMDUzMC0xZjE4LTRkNzMtOTNmNS0zM2MxM2NjMDUxNTUiLCAiaG9zdCI6ICJldV9zaGFyZWQifQ.Xzh4Qjz0FMRDXFuFIWPI-3DWEC6y-RFBfm_wE3_Qp2M

With the payload and Admin Token above, the signed JWT payload would look like this:

Example JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3b3Jrc3BhY2VfaWQiOiIzMTA0OGI3Ni01MmU4LTQ5N2ItOTBhNC0wYzZhNTUxMzkyMGQiLCJuYW1lIjoiZnJvbnRlbmRfand0IiwiZXhwIjoxMjMxMjMxMjMxMjMsInNjb3BlcyI6W3sidHlwZSI6IlBJUEVTOlJFQUQiLCJyZXNvdXJjZSI6ImVhNDdmZDlkLWJjNDgtNDIwZC1hNmY2LTk1NDgxZmJiM2Y3YyIsImZpeGVkX3BhcmFtcyI6eyJvcmdfaWQiOiJ0ZXN0aW5nIn19XSwiaWF0IjoxNzE3MDYzNzQwfQ.t-9BRLI6MrhOAuvt1mBSTBTU7TOdJFunBjr78TuqpVg

You can use the JWT.io debugger to verify the above example.

JWT limitations

  • You cannot refresh JWTs individually from inside Tinybird as they are not stored in Tinybird. You must do this from your application, or you can globally invalidate all JWTs by refreshing your Admin Token.
  • If you refresh your Admin Token, all the tokens will be invalidated.
  • If your token is expired or invalidated, you'll get a 403 HTTP status code from Tinybird when requesting the API Endpoint.

Create a JWT

Create a JWT in production

There is wide support for creating JWTs in many programming languages and frameworks. Any library that supports JWTs should work with Tinybird.

A common library to use with Python is PyJWT. Common libraries for JavaScript are jsonwebtoken and jose.

Create a JWT in Python using pyjwt
import jwt
import datetime
import os

TINYBIRD_SIGNING_KEY = os.getenv('TINYBIRD_SIGNING_KEY')

def generate_jwt():
  expiration_time = datetime.datetime.utcnow() + datetime.timedelta(hours=3)
  payload = {
      "workspace_id": "31048b76-52e8-497b-90a4-0c6a5513920d",
      "name": "frontend_jwt",
      "exp": expiration_time,
      "scopes": [
          {
              "type": "PIPES:READ",
              "resource": "requests_per_day",
              "fixed_params": {
                  "org_id": "testing"
              }
          }
      ]
  }

  return jwt.encode(payload, TINYBIRD_SIGNING_KEY, algorithm='HS256')

Create a JWT using the CLI or the API

If for any reason you don't want to generate a JWT on your own, Tinybird provides an API and a CLI utility to create JWTs.

Create a JWT with the Tinybird CLI
tb token create jwt my_jwt --ttl 1h --scope PIPES:READ --resource my_pipe --filters "column_name=value"

Error handling

There are many reasons why a request might return a 403 status code. When a 403 is received, check the following:

  1. Confirm the JWT is valid and has not expired (the expiration time is in the exp field in the JWT's payload).
  2. The generated JWTs can only read Tinybird API Endpoints. Confirm you're not trying to use the JWT to access other APIs.
  3. Confirm the JWT has a scope to read the endpoint you are trying to read. Check the payload of the JWT at https://jwt.io/.
  4. If you generated the JWT outside of Tinybird (without using the API or the CLI), make sure you are using the Workspace admin token, not your personal one.

Rate limits for JWTs

Check the limits page for limits on ingestion, queries, API Endpoints, and more.

When you specify a limits.rps field in the payload of the JWT, Tinybird will use the name specified in the payload of the JWT to track the number of requests being done. If the number of requests goes above the limit, Tinybird starts rejecting new requests and returns an "HTTP 429 Too Many Requests" error. Check the limits docs for more information.

The following example shows the tracking of all requests done by frontend_jwt. Once you reach 10 requests per second, Tinybird would start rejecting requests:

Example payload with global rate limit
{
    "workspace_id": "31048b76-52e8-497b-90a4-0c6a5513920d",
    "name": "frontend_jwt",
    "exp": 123123123123,
    "scopes": [
        {
            "type": "PIPES:READ",
            "resource": "requests_per_day",
            "fixed_params": {
                "org_id": "testing"
            }
        }
    ],
    "limits": {
      "rps": 10
    }
}

If rps <= 0, Tinybird ignores the limit and assumes there is no limit.

As the name field does not have to be unique, all the tokens generated using the name=frontend_jwt would be under the same umbrella. This can be very useful if you want to have a global limit in one of your apps or components.

If you want to just limit for each specific user, you could generate a JWT using the following payload. In this case, you would specify a unique name so the limits only apply to each user:

Example of a payload with isolated rate limit
{
    "workspace_id": "31048b76-52e8-497b-90a4-0c6a5513920d",
    "name": "frontend_jwt_user_<unique identifier>",
    "exp": 123123123123,
    "scopes": [
        {
            "type": "PIPES:READ",
            "resource": "requests_per_day",
            "fixed_params": {
                "org_id": "testing"
            }
        }
    ],
    "limits": {
      "rps": 10
    }
}

Next steps