Getting started

Installation

Download the Node.js SDK using your favorite package manager.

npm install @kobbleio/admin

Get your secret key

You can obtain your secret key from your dashboard.

Secret key page

Test your integration

import { Kobble } from '@kobbleio/admin'

const main = async () => {
    const kobble = new Kobble('YOUR_SECRET_KEY');
    const whoami = await kobble.whoami();

    console.log(whoami);
}

Authentication

These methods allow you to verify that incoming requests on your server come from authenticated and legitimate users.

You can pass either ID Token or Access Token to your backend. To get these tokens, you can use the frontend SDKs.

The ID token is a JWT token that contains the user’s identity information.

The Access Token is a JWT token that only contains the user id and project id.

These token are short-lived and should be verified on each request. Never store these tokens backend-side. Keep in mind that the id token may contain some out of date information since it’s not generated at each request.

Verify ID Token

You can verify ID tokens obtained using Kobble frontend SDKs as follows:

const result = await kobble.auth.verifyIdToken('ID_TOKEN');
// Example output:
// {
//     userId: 'clu9ob5480001mdhwk9qt00hv',
//     user: {
//         id: 'clu9ob5480001mdhwk9qt00hv',
//         email: 'kevinpiac@gmail.com',
//         name: null,
//         pictureUrl: null,
//         isVerified: true,
//         stripeId: null,
//         updatedAt: 2024-03-27T10:39:02.000Z,
//         createdAt: 2024-03-27T10:39:02.000Z
//     },
//     claims: { ... }
// }
}

Verify Access Token

You can verify access tokens obtained using Kobble frontend SDKs as follows:

const result = await kobble.auth.verifyAccessToken('ACCESS_TOKEN');

console.log(result)
// Example output:
// {
//     projectId: 'cltxiphfv000129anb0kuagow',
//     userId: 'clu9ob5480001mdhwk9qt00hv',
//     claims: {
//         sub: 'clu9ob5480001mdhwk9qt00hv',
//         project_id: 'cltxiphfv000129anb0kuagow',
//         iat: 1713183109,
//         exp: 1713186709689,
//         iss: 'https://kobble.io',
//         aud: 'clu9ntcvr0000o9yfz87ybo4a'
//     }
// }

Users

List users

This method allows you to list all users in your project. For performance reasons, the method will return a maximum of 100 users.

const { total, count, hasNext, page, data } = await kobble.data.listAll({
    limit: 10, // default 50, max 100
    page: 4, // default 1
})

console.log(data);
// Example output:
// [{
//   id: 'clu9ob5480001mdhwk9qt00hv';
//   email: 'hello@kobble.io';
//   name: 'Kevin Piacentini';
//   createdAt: Date;
//   isVerified: true;
// }]

Get user by ID

The getById function is an asynchronous method used to retrieve a user by their unique identifier. This function accepts two parameters: id and an optional options object.

It throws an error if the user does not exist.
Parameters
  • id (string): The unique identifier of the user to be retrieved.
  • options (object, optional): An optional object that can contain the following properties:
    • includeMetadata (boolean, optional): A flag indicating whether to include metadata in the retrieved user information. Default is false.
Returns
  • Promise<User>: A promise that resolves to a User object containing the user’s information.
Example Usage
try {
 const user = await kobble.users.getById('USER_ID', { includeMetadata: true });

 console.log(user);
} catch (error) {
    console.error('User not found');
}

// Example output:
// {
//   id: 'clu9ob5480001mdhwk9qt00hv';
//   email: 'hello@kobble.io';
//   name: 'Kevin Piacentini';
//   createdAt: Date;
//   isVerified: true;
//   metadata: {};
// };

Find users by Metadata

The findByMetadata function is an asynchronous method used to retrieve users based on specific metadata criteria. This function accepts two parameters: metadata and an optional options object.

Parameters
  • metadata (Record<string, string>): An object containing the metadata key-value pairs to filter users. The keys and values are both strings.
  • options (object, optional): An optional object that can contain the following properties:
    • page (number, optional): The page number for pagination. Default is 1.
    • limit (number, optional): The number of users to retrieve per page. Default is 50.
Returns
  • Promise<Paginated<User>>: A promise that resolves to a paginated object containing the users that match the metadata criteria.
Example Usage

This is how you can find all your users having a location set to ‘New York’ and a preference set to ‘dark’:

try {
  const metadata = {
    location: 'New York',
    preference: 'dark'
  };
  const options = { page: 2, limit: 10 };

  const result = await kobble.users.findByMetadata(metadata, options);

  console.log(result);
} catch (error) {
  console.error('Failed to find users by metadata', error);
}

// Example output:
// {
//   total: 100,
//   page: 2,
//   limit: 10,
//   data: [
//     {
//       id: 'clu9ob5480001mdhwk9qt00hv',
//       email: 'hello@kobble.io',
//       name: 'Kevin Piacentini',
//       createdAt: Date,
//       isVerified: true,
//       metadata: {
//         location: 'New York',
//         preference: 'dark'
//       }
//     },
//     // ...more users
//   ]
// }
Limitations
Even if the metadata payload can up to 1MB, we recommend keeping your field values as small as possible, especially if you want to search for them. Storing large values in metadata fields can slow down the search process and reveal performance issues on your tenant.

Update user metadata

You can use the updateMetadata method to store data into a user.

Unlike patchMetadata, which atomically updates the metadata, this method replaces the entire payload stored in the user’s metadata field with the new data provided.

The whole metadata payload must not exceed 1MB, which must be enough for most use cases.
Parameters
  • userId (string): The unique identifier of the user whose metadata needs to be updated.
  • metadata (Record<string, any>): An object containing the metadata key-value pairs to be updated for the user. The keys are strings, and the values can be of any type.
Returns
  • void: This function does not return a value. It performs the update operation on the specified user’s metadata.
Example Usage
try {
  const userId = 'USER_ID';
  const metadata = {
    age: 30,
    location: 'New York',
    preferences: {
      theme: 'dark',
      notifications: true
    }
  };

  kobble.users.updateMetadata(userId, metadata);
  console.log('User metadata updated successfully');
} catch (error) {
  console.error('Failed to update user metadata', error);
}

Patch user metadata

You can use the patchMetadata method to atomically update the metadata payload of the user.

Unlike updateMetadata, which replaces the whole metadata payload, this method replaces only the provided fields.

The whole metadata payload must not exceed 1MB, which must be enough for most use cases.
Parameters
  • userId (string): The unique identifier of the user whose metadata needs to be updated.
  • metadata (Record<string, any>): An object containing the metadata key-value pairs to be updated for the user. The keys are strings, and the values can be of any type.
Returns
  • void: This function does not return a value. It performs the update operation on the specified user’s metadata.
Example Usage
try {
  const userId = 'USER_ID';
  const metadata = {
    age: 30,
    location: 'New York',
    preferences: {
      theme: 'dark',
      notifications: true
    }
  };

  kobble.users.updateMetadata(userId, metadata);
  console.log('User metadata updated successfully');
} catch (error) {
  console.error('Failed to update user metadata', error);
}

Get user product

This method will fetch the product currently attached to the user. It may be a free product or a paid product.

const product = await kobble.users.getActiveProduct('USER_ID');

console.log(product);
// Example output:
// {
//   id: 'my-product-id',
//   name: 'My Product',
// }

Quotas

List user quotas

This method will fetch all the quotas usage of the product attached to the user. It won’t fetch quotas not attached to the product of the user.

const quotas = await kobble.users.listQuotas('USER_ID');

console.log(quotas);
// Example output:
// [{
//   name: 'My Quota',
//   usage: 100,
//   expiresAt: Date,
//   remaining: 10,
//   limit: 90,
// }]

Check Remaining Quotas

The hasRemainingQuota method checks if a user has remaining credit for the specified quota(s).

This is useful to prevent users from exceeding their assigned limits in your application.

You can pass an array of quota names or a single quota name as a string.

const userId = 'user123';
const quotaNames = ['api-calls', 'data-storage'];

const hasCredit = await kobble.users.hasRemainingQuota(userId, quotaNames);

console.log(hasCredit); // Output: true or false, depending on quota availability

Increment Quota Usage

The incrementQuotaUsage method allows you to increment the usage of a quota for a user.

This is useful when you want to track the usage of a specific quota in your application.

const userId = 'user123';
const quotaName = 'image-generated';

await kobble.users.incrementQuotaUsage(userId, quotaName);

By default the increment is 1, but you can specify a different increment value.

await kobble.users.incrementQuotaUsage(userId, quotaName, 5);

Decrement Quota Usage

In rare scenarios, you may need to decrement the usage of a quota for a user.

const userId = 'user123';
const quotaName = 'image-generated';

await kobble.users.decrementQuotaUsage(userId, quotaName);

Set Quota Usage

The setQuotaUsage method allows you to set the usage of a quota for a user.

This is only useful when you need to set the usage of a quota to a specific value that may be lower or higher than the current usage.

const userId = 'user123';
const quotaName = 'image-generated';

// New usage will be 10, no matter the current usage
await kobble.users.setQuotaUsage(userId, quotaName, 10);

Permissions

List user permissions

This method will fetch all the permissions of the product attached to the user. As for quotas, it won’t fetch permissions not attached to the product of the user.

const permissions = await kobble.users.listPermissions('USER_ID');

console.log(permissions);
// Example output:
// [{ name: 'generate-image' }, { name: 'generate-pdf' }]

Check Permissions

The hasPermission method allows you to verify if a user has all the specified permissions. This is particularly useful for authorization checks within your application.

You can pass an array of permission or a single permission string.

const userId = 'user123';
const permissions = ['generate-image', 'generate-pdf'];

const hasPermission = await kobble.users.hasPermission(userId, permissions);

console.log(hasPermission); // Output: true or false, depending on permission availability

Webhooks

The Admin SDK provides an utility function that verifies the signature of incoming webhook events and returns a typed event object.

Construct Event

You can use the webhooks.constructEvent() method to handle incoming webhook events from Kobble.

The list of events you can handle is available in the webhook events section.

Here’s an example with express:

import express from 'express';
import { Kobble } from '@kobbleio/admin'

const kobble = new Kobble('YOUR_SECRET_KEY');

// Replace this endpoint secret with your endpoint's unique secret
// If you are using an endpoint defined with the API or dashboard, look in your webhook settings
// at https://app.kobble.io/p/webhooks
const endpointSecret = '...';

const app = express();

app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
    let event = request.body;

    const signature = request.headers['Kobble-Signature'];
    try {
        event = kobble.webhooks.constructEvent(
            request.body,
            signature,
            endpointSecret
        );
    } catch (err) {
        console.log(`⚠️ Webhook signature verification failed.`, err.message);
        return response.sendStatus(400);
    }

    // Handle the event
    switch (event.type) {
        case 'user.created':
          const user = event.data;
          console.log(`User created with email: ${user.email}.`);
          // Do something...
          break;
        case 'quota.reached':
          const data = event.data;
          console.log(`User ${data.userId} reached quota ${data.quotaName}.`);
          // Do something...
          break;
        default:
          // Unexpected event type
          console.log(`Unhandled event type ${event.type}.`);
  }

  // Return a 200 response to acknowledge receipt of the event
  response.send();
});

app.listen(3000, () => console.log('Running on port 3000'));