﻿<!-- Source: https://developers.memberstack.com/admin-rest-api/data-tables -->
<!-- Markdown version of a Memberstack developer documentation page. Canonical HTML: https://developers.memberstack.com/admin-rest-api/data-tables -->

# Data Tables

The Memberstack Admin REST API provides powerful endpoints for managing Data Tables. Data Tables allow you to store structured data with support for relationships, access control rules, and advanced querying capabilities.

Before You Start

-   Make sure you have your secret key ready (refer to the [Quick Start](/admin-rest-api/quick-start) guide for authentication details)
-   All Data Tables endpoints use the `/v2` API version
-   Be mindful of the rate limit (25 requests per second)
-   Data Tables must be created in the Memberstack dashboard (Data Tables section) before using the API

## Overview

Data Tables API endpoints and capabilities.

The Data Tables API provides six endpoints for complete data management:

| Method | Endpoint | Description |
| --- | --- | --- |
| GET | `/v2/data-tables` | List all data tables |
| GET | `/v2/data-tables/:tableKey` | Get a specific table by key or ID |
| POST | `/v2/data-tables/:tableKey/records` | Create a new record |
| POST | `/v2/data-tables/:tableKey/records/query` | Query records with filters |
| PUT | `/v2/data-tables/:tableKey/records/:recordId` | Update an existing record |
| DELETE | `/v2/data-tables/:tableKey/records/:recordId` | Delete a record |

### Field Types

Data Tables support the following field types:

-   `TEXT` - String values
-   `TEXT_FILTERABLE` - Short text optimized for filtering (255 character max)
-   `TEXT_UNIQUE` - String values with unique constraint
-   `NUMBER` - Numeric values
-   `DECIMAL` - Decimal/currency values
-   `BOOLEAN` - True/false values
-   `DATE` - Date and time values
-   `EMAIL` - Email address values
-   `URL` - URL values
-   `REFERENCE` - Single record relationship
-   `REFERENCE_MANY` - Multiple record relationships
-   `MEMBER_REFERENCE` - Single member relationship
-   `MEMBER_REFERENCE_MANY` - Multiple member relationships

## List Data Tables

Retrieve all data tables in your application.

GET /v2/data-tables Copy

Retrieve all data tables in your application.

Example request

curlAxios Copy

```bash
curl --location --request GET 'https://admin.memberstack.com/v2/data-tables' \
--header 'x-api-key: sk_sb_your_secret_key'
```

```js
const axios = require('axios');

const API_KEY = process.env.MEMBERSTACK_SECRET_KEY;
const BASE_URL = 'https://admin.memberstack.com/v2/data-tables';
const headers = { "X-API-KEY": API_KEY };

const response = await axios.get(BASE_URL, { headers });
console.log(response.data);
```

Example response

application/json 200 OK

```
{
  "data": {
    "tables": [
      {
        "id": "tbl_cm1abc123def456",
        "key": "products",
        "name": "Products",
        "createRule": "AUTHENTICATED",
        "readRule": "PUBLIC",
        "updateRule": "AUTHENTICATED_OWN",
        "deleteRule": "ADMIN_ONLY",
        "createdAt": "2024-01-15T10:30:00.000Z",
        "updatedAt": "2024-01-20T14:45:00.000Z",
        "fields": [
          {
            "id": "cm2xyz789ghi012abc",
            "key": "name",
            "name": "Product Name",
            "type": "TEXT",
            "required": true,
            "defaultValue": null,
            "tableOrder": 0,
            "referencedTableId": null
          },
          {
            "id": "cm3def456jkl789xyz",
            "key": "price",
            "name": "Price",
            "type": "DECIMAL",
            "required": true,
            "defaultValue": null,
            "tableOrder": 0,
            "referencedTableId": null
          },
          {
            "id": "cm4ghi789mno012def",
            "key": "category",
            "name": "Category",
            "type": "REFERENCE",
            "required": false,
            "defaultValue": null,
            "tableOrder": 0,
            "referencedTableId": "tbl_cm5cat001pqr345",
            "referencedTable": {
              "id": "tbl_cm5cat001pqr345",
              "key": "categories",
              "name": "Categories"
            }
          }
        ]
      }
    ]
  }
}
```

https://admin.memberstack.com/v2/data-tables Try it

## Get Data Table

Retrieve a specific data table by key or ID.

URL Parameters

Replace `:tableKey` with either:

-   Table key (e.g., `products`)
-   Table ID (e.g., `tbl_cm1abc123def456`)

GET /v2/data-tables/{tableKey} Copy

Retrieve a specific data table by key or ID.

Parameters

:tableKeystringRequired

The table key (e.g. `products`) or table ID (e.g. `tbl_cm1abc123def456`).

Example request

curlAxios Copy

```bash
curl --location --request GET 'https://admin.memberstack.com/v2/data-tables/products' \
--header 'x-api-key: sk_sb_your_secret_key'
```

```js
const axios = require('axios');

const API_KEY = process.env.MEMBERSTACK_SECRET_KEY;
const BASE_URL = 'https://admin.memberstack.com/v2/data-tables';
const headers = { "X-API-KEY": API_KEY };

// Get by table key
const response = await axios.get(`${BASE_URL}/products`, { headers });

// Or get by table ID
const responseById = await axios.get(`${BASE_URL}/tbl_cm1abc123def456`, { headers });
```

Example response

application/json 200 OK

```
{
  "data": {
    "id": "tbl_cm1abc123def456",
    "key": "products",
    "name": "Products",
    "createRule": "AUTHENTICATED",
    "readRule": "PUBLIC",
    "updateRule": "AUTHENTICATED_OWN",
    "deleteRule": "ADMIN_ONLY",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-20T14:45:00.000Z",
    "fields": [
      {
        "id": "cm2xyz789ghi012abc",
        "key": "name",
        "name": "Product Name",
        "type": "TEXT",
        "required": true,
        "defaultValue": null,
        "tableOrder": 0,
        "referencedTableId": null
      }
    ]
  }
}
```

https://admin.memberstack.com/v2/data-tables/{tableKey} Try it

## Create Data Record

Create a new record in a data table. Replace `:tableKey` with the table key or ID.

Request Body

dataobjectRequired

Object containing field key-value pairs

memberIdstring

ID of the member to assign as the record owner. The member must belong to the same app and environment (live/sandbox). If omitted, the record will have no member owner (displayed as "System").

POST /v2/data-tables/{tableKey}/records Copy

Create a new record in a data table.

Parameters

:tableKeystringRequired

The table key or ID.

Example request

curlcurl (with owner)Axios Copy

```bash
curl --location --request POST 'https://admin.memberstack.com/v2/data-tables/products/records' \
--header 'x-api-key: sk_sb_your_secret_key' \
--header 'Content-Type: application/json' \
--data-raw '{
  "data": {
    "name": "Premium Widget",
    "price": 29.99,
    "inStock": true,
    "category": "cm6cat001stu678abc"
  }
}'
```

```bash
curl --location --request POST 'https://admin.memberstack.com/v2/data-tables/products/records' \
--header 'x-api-key: sk_sb_your_secret_key' \
--header 'Content-Type: application/json' \
--data-raw '{
  "data": {
    "name": "Premium Widget",
    "price": 29.99,
    "inStock": true
  },
  "memberId": "mem_abc123"
}'
```

```js
const axios = require('axios');

const API_KEY = process.env.MEMBERSTACK_SECRET_KEY;
const BASE_URL = 'https://admin.memberstack.com/v2/data-tables';
const headers = {
  "X-API-KEY": API_KEY,
  "Content-Type": "application/json"
};

// Without member ownership (record will show "System" as creator)
const response = await axios.post(`${BASE_URL}/products/records`, {
  data: {
    name: "Premium Widget",
    price: 29.99,
    inStock: true,
    category: "cm6cat001stu678abc"  // Reference to another record
  }
}, { headers });

// With member ownership
const ownedResponse = await axios.post(`${BASE_URL}/products/records`, {
  data: {
    name: "Member's Widget",
    price: 29.99
  },
  memberId: "mem_abc123"
}, { headers });

console.log(response.data);
```

Example response

application/json 200 OK

```
{
  "data": {
    "id": "cm7new123vwx901def",
    "tableKey": "products",
    "data": {
      "name": "Premium Widget",
      "price": "29.99",
      "inStock": true,
      "category": "cm6cat001stu678abc"
    },
    "createdAt": "2024-01-25T09:15:00.000Z",
    "updatedAt": "2024-01-25T09:15:00.000Z",
    "internalOrder": 12345
  }
}
```

https://admin.memberstack.com/v2/data-tables/{tableKey}/records Try it

Response with `memberId` (note the `createdByMemberId` field):

Response (with memberId) Copy

```json
{
  "data": {
    "id": "cm7new456xyz789ghi",
    "tableKey": "products",
    "createdByMemberId": "mem_abc123",
    "data": {
      "name": "Member's Widget",
      "price": "29.99"
    },
    "createdAt": "2024-01-25T09:15:00.000Z",
    "updatedAt": "2024-01-25T09:15:00.000Z",
    "internalOrder": 12346
  }
}
```

memberId validation

The `memberId` parameter is validated against the current app and environment:

-   The member must exist in the same app as the secret key
-   The member’s environment must match the key type (live key = live members, sandbox key = sandbox members)
-   Returns **404** with `"Member not found"` if the member does not exist or fails validation
-   Returns **400** with `"memberId must be a non-empty string"` if the value is not a valid string

Tips for creating records

-   Use `memberId` when migrating data into tables with `AUTHENTICATED_OWN` access rules so members can access their own records
-   Fields with default values will be automatically populated if not provided
-   For REFERENCE fields, provide the referenced record’s ID
-   For REFERENCE\_MANY fields, provide an array of record IDs
-   Required fields must be provided or have a default value
-   DECIMAL fields are returned as **strings** in create, update, and delete responses (e.g. `"29.99"`); the query endpoint returns them as numbers.

## Update Data Record

Update an existing record in a data table. Replace `:tableKey` with the table key or ID, and `:recordId` with the record ID.

Request Body

dataobjectRequired

Object containing field key-value pairs to update (cannot be empty)

PUT /v2/data-tables/{tableKey}/records/{recordId} Copy

Update an existing record in a data table.

Parameters

:tableKeystringRequired

The table key or ID.

:recordIdstringRequired

The record ID.

Example request

curlAxios Copy

```bash
curl --location --request PUT 'https://admin.memberstack.com/v2/data-tables/products/records/cm8abc123yza234ghi' \
--header 'x-api-key: sk_sb_your_secret_key' \
--header 'Content-Type: application/json' \
--data-raw '{
  "data": {
    "price": 39.99,
    "inStock": false
  }
}'
```

```js
const axios = require('axios');

const API_KEY = process.env.MEMBERSTACK_SECRET_KEY;
const BASE_URL = 'https://admin.memberstack.com/v2/data-tables';
const headers = {
  "X-API-KEY": API_KEY,
  "Content-Type": "application/json"
};

const response = await axios.put(
  `${BASE_URL}/products/records/cm8abc123yza234ghi`,
  {
    data: {
      price: 39.99,
      inStock: false
    }
  },
  { headers }
);

console.log(response.data);
```

Example response

application/json 200 OK

```
{
  "data": {
    "id": "cm8abc123yza234ghi",
    "tableKey": "products",
    "data": {
      "name": "Premium Widget",
      "price": "39.99",
      "inStock": false,
      "category": "cm6cat001stu678abc"
    },
    "createdAt": "2024-01-25T09:15:00.000Z",
    "updatedAt": "2024-01-26T11:30:00.000Z",
    "internalOrder": 12345
  }
}
```

https://admin.memberstack.com/v2/data-tables/{tableKey}/records/{recordId} Try it

Tips for updating records

-   Updates are partial - only include the fields you want to change
-   The data object cannot be empty
-   Unique constraints are validated (excluding the current record)
-   Reference constraints are validated for relationship fields

## Delete Data Record

Delete a record from a data table. Replace `:tableKey` with the table key or ID, and `:recordId` with the record ID.

DELETE /v2/data-tables/{tableKey}/records/{recordId} Copy

Delete a record from a data table.

Parameters

:tableKeystringRequired

The table key or ID.

:recordIdstringRequired

The record ID.

Example request

curlAxios Copy

```bash
curl --location --request DELETE 'https://admin.memberstack.com/v2/data-tables/products/records/cm8abc123yza234ghi' \
--header 'x-api-key: sk_sb_your_secret_key'
```

```js
const axios = require('axios');

const API_KEY = process.env.MEMBERSTACK_SECRET_KEY;
const BASE_URL = 'https://admin.memberstack.com/v2/data-tables';
const headers = { "X-API-KEY": API_KEY };

const response = await axios.delete(
  `${BASE_URL}/products/records/cm8abc123yza234ghi`,
  { headers }
);

console.log(response.data);
```

Example response

application/json 200 OK

```
{
  "data": {
    "id": "cm8abc123yza234ghi",
    "tableKey": "products",
    "data": {
      "name": "Premium Widget",
      "price": "39.99",
      "inStock": false,
      "category": "cm6cat001stu678abc"
    },
    "createdAt": "2024-01-25T09:15:00.000Z",
    "updatedAt": "2024-01-26T11:30:00.000Z",
    "internalOrder": 12345
  }
}
```

https://admin.memberstack.com/v2/data-tables/{tableKey}/records/{recordId} Try it

Warning: deletion is permanent

Deleting a record is permanent and cannot be undone.

-   The deleted record data is returned in the response
-   Consider the impact on related records before deletion
-   Implement soft-delete in your application if you need to preserve data

## Query Data Records

Query records with advanced filtering, sorting, and pagination. Replace `:tableKey` with the table key or ID.

### Request Body

The query endpoint uses a Prisma-like query syntax with either `findMany` or `findUnique`.

| Parameter | Type | Description |
| --- | --- | --- |
| `query.findMany` | object | Query multiple records (mutually exclusive with findUnique) |
| `query.findUnique` | object | Query a single record by ID (mutually exclusive with findMany) |

### Query Options (findMany)

| Option | Type | Description |
| --- | --- | --- |
| `where` | object | Filter conditions |
| `include` | object | Include related records or counts |
| `select` | object | Select specific fields (cannot use with include) |
| `orderBy` | object \| array | Sort results |
| `take` | number | Limit number of results (max 100) |
| `skip` | number | Offset pagination (max 10000) |
| `after` | number \| string | Cursor-based pagination (cannot use with skip) |
| `_count` | boolean | Return only the count of matching records |

### Where Operators

Filter conditions support the following operators:

| Operator | Description | Example |
| --- | --- | --- |
| `equals` | Exact match | `{ price: { equals: 29.99 } }` |
| `not` | Not equal | `{ status: { not: 'archived' } }` |
| `in` | In array | `{ status: { in: ['active', 'pending'] } }` |
| `notIn` | Not in array | `{ status: { notIn: ['deleted'] } }` |
| `lt` | Less than | `{ price: { lt: 100 } }` |
| `lte` | Less than or equal | `{ price: { lte: 100 } }` |
| `gt` | Greater than | `{ price: { gt: 50 } }` |
| `gte` | Greater than or equal | `{ price: { gte: 50 } }` |
| `contains` | Contains substring | `{ name: { contains: 'widget' } }` |
| `startsWith` | Starts with | `{ name: { startsWith: 'Premium' } }` |
| `endsWith` | Ends with | `{ name: { endsWith: 'Pro' } }` |

Logical operators `AND`, `OR`, and `NOT` can be used to combine conditions.

POST /v2/data-tables/{tableKey}/records/query Copy

Query records with advanced filtering, sorting, and pagination.

Parameters

:tableKeystringRequired

The table key or ID.

Example request

Basic queryPaginationRelated recordsLogical operatorsCountFind uniqueDate range Copy

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      where: {
        inStock: true,
        price: { gte: 20, lte: 100 }
      },
      orderBy: { price: 'asc' },
      take: 10
    }
  }
}, { headers });
```

```js
// First page
const firstPage = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      take: 20,
      orderBy: { createdAt: 'desc' }
    }
  }
}, { headers });

// Next page using cursor
const nextPage = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      take: 20,
      after: firstPage.data.data.pagination.endCursor,
      orderBy: { createdAt: 'desc' }
    }
  }
}, { headers });
```

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      where: { inStock: true },
      include: {
        category: true,  // Include referenced category
        _count: {
          select: {
            orders: true  // Count related orders
          }
        }
      }
    }
  }
}, { headers });
```

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      where: {
        OR: [
          { price: { lt: 10 } },
          { AND: [
            { price: { gte: 50 } },
            { inStock: true }
          ]}
        ]
      }
    }
  }
}, { headers });
```

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      where: { inStock: true },
      _count: true
    }
  }
}, { headers });

// Response: { "data": { "_count": 42 } }
```

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findUnique: {
      where: { id: "cm8abc123yza234ghi" },
      include: {
        category: true
      }
    }
  }
}, { headers });
```

```js
const response = await axios.post(`${BASE_URL}/products/records/query`, {
  query: {
    findMany: {
      where: {
        createdAt: {
          gte: "2024-01-01T00:00:00.000Z",
          lt: "2024-02-01T00:00:00.000Z"
        }
      }
    }
  }
}, { headers });
```

Example response

application/json 200 OK

```
{
  "data": {
    "records": [
      {
        "id": "cm8abc123yza234ghi",
        "internalOrder": 12345,
        "createdAt": "2024-01-25T09:15:00.000Z",
        "updatedAt": "2024-01-26T11:30:00.000Z",
        "data": {
          "name": "Premium Widget",
          "price": 29.99,
          "inStock": true
        }
      }
    ],
    "pagination": {
      "hasMore": true,
      "limit": 20,
      "endCursor": 12345
    }
  }
}
```

https://admin.memberstack.com/v2/data-tables/{tableKey}/records/query Try it

### Response (findUnique)

findUnique response Copy

```json
{
  "data": {
    "record": {
      "id": "cm8abc123yza234ghi",
      "internalOrder": 12345,
      "createdAt": "2024-01-25T09:15:00.000Z",
      "updatedAt": "2024-01-26T11:30:00.000Z",
      "data": {
        "name": "Premium Widget",
        "price": 29.99,
        "inStock": true,
        "category": {
          "id": "cm6cat001stu678abc",
          "data": {
            "name": "Electronics"
          }
        }
      }
    }
  }
}
```

Query best practices

-   Use cursor-based pagination (`after`) for better performance on large datasets
-   Limit your `take` value to only what you need (max 100)
-   Use `select` to retrieve only the fields you need
-   Maximum include depth is 3 levels
-   Maximum of 10 includes per query
-   Maximum of 50 where conditions per query

Query limitations

-   Cannot use both `skip` and `after` together
-   Cannot use both `include` and `select` together
-   `findUnique` only allows `id` in the where clause
-   `findUnique` does not support `take`, `skip`, `after`, or `orderBy`
-   `findUnique` does not support top-level `_count` (use within includes instead)
-   REFERENCE\_MANY and MEMBER\_REFERENCE\_MANY includes are only available in `findUnique`

## Error Handling

The API returns consistent error responses with a `code` and `message`:

Error response Copy

```json
{
  "code": "generic-message",
  "message": "Human-readable description of what went wrong"
}
```

For Data Tables endpoints, `code` is `generic-message` for virtually all errors (validation and not-found alike), so branch on the HTTP status and read the `message` field for specifics. The shared auth layer is the exception: a missing or malformed secret key returns `validation/invalid-secret-key` and a malformed JSON body returns `validation/invalid-json`.

### Common Errors

| Status | Error | Description |
| --- | --- | --- |
| 400 | Table key is required | Missing table key in URL |
| 400 | Record ID is required | Missing record ID in URL |
| 400 | data must be an object | Invalid data format in request body |
| 400 | data cannot be empty | Update request with empty data object |
| 400 | Query parameter is required | Missing query in request body |
| 400 | Invalid table key or ID format | Table key contains invalid characters |
| 400 | take must be between 1 and 100 | Query take value out of range |
| 400 | Either query.findMany or query.findUnique parameter is required | Missing query type in request body |
| 400 | Cannot specify both query.findMany and query.findUnique | Both query types provided |
| 400 | query.findUnique requires where.id | Missing id in findUnique where clause |
| 404 | Data table not found | Table doesn't exist or wrong app |
| 404 | Data record not found | Record doesn't exist |
| 404 | Record not found | findUnique query returned no results |

### Error Handling Example

error-handling.js Copy

```js
try {
  const response = await axios.post(`${BASE_URL}/products/records`, {
    data: {
      name: "New Product",
      price: 29.99
    }
  }, { headers });

  console.log('Record created:', response.data);
} catch (error) {
  if (error.response) {
    // Server responded with error
    console.error('Error code:', error.response.data.code);
    console.error('Error message:', error.response.data.message);
    console.error('Status:', error.response.status);

    switch (error.response.status) {
      case 400:
        // Handle validation errors
        break;
      case 404:
        // Handle not found errors
        break;
      default:
        // Handle other errors
    }
  } else {
    // Network or other error
    console.error('Network error:', error.message);
  }
}
```

Next steps

[

Quick Start

Authentication and your first request

](/admin-rest-api/quick-start)[

Member Actions

Create, read, update, and delete members

](/admin-rest-api/member-actions)[

Common Use Cases

Practical recipes for the Admin API

](/admin-rest-api/common-use-cases)[

FAQs

Answers to common questions

](/admin-rest-api/faqs)

Need help?

Can't find what you're looking for? Our team is here to help.

[Join the Slack community](https://www.memberstack.com/slack)[Find an expert](https://www.memberstack.com/experts)

[

Previous

Member Actions

](/admin-rest-api/member-actions)[

Next

Verification

](/admin-rest-api/verificatio