Skip to main content
Memberstack Docs
Dashboard

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.

/admin-rest-api/data-tables.md
Before You Start
  • Make sure you have your secret key ready (refer to the 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:

MethodEndpointDescription
GET/v2/data-tablesList all data tables
GET/v2/data-tables/:tableKeyGet a specific table by key or ID
POST/v2/data-tables/:tableKey/recordsCreate a new record
POST/v2/data-tables/:tableKey/records/queryQuery records with filters
PUT/v2/data-tables/:tableKey/records/:recordIdUpdate an existing record
DELETE/v2/data-tables/:tableKey/records/:recordIdDelete 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

Retrieve all data tables in your application.

curl --location --request GET 'https://admin.memberstack.com/v2/data-tables' \
--header 'x-api-key: sk_sb_your_secret_key'
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

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}

Retrieve a specific data table by key or ID.

:tableKeystringRequired
The table key (e.g. products) or table ID (e.g. tbl_cm1abc123def456).
curl --location --request GET 'https://admin.memberstack.com/v2/data-tables/products' \
--header 'x-api-key: sk_sb_your_secret_key'
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}

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

Create a new record in a data table.

:tableKeystringRequired
The table key or ID.
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"
  }
}'
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

Response with memberId (note the createdByMemberId field):

{
  "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}

Update an existing record in a data table.

:tableKeystringRequired
The table key or ID.
:recordIdstringRequired
The record ID.
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
  }
}'
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}
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}

Delete a record from a data table.

:tableKeystringRequired
The table key or ID.
:recordIdstringRequired
The record ID.
curl --location --request DELETE 'https://admin.memberstack.com/v2/data-tables/products/records/cm8abc123yza234ghi' \
--header 'x-api-key: sk_sb_your_secret_key'
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}
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.

ParameterTypeDescription
query.findManyobjectQuery multiple records (mutually exclusive with findUnique)
query.findUniqueobjectQuery a single record by ID (mutually exclusive with findMany)

Query Options (findMany)

OptionTypeDescription
whereobjectFilter conditions
includeobjectInclude related records or counts
selectobjectSelect specific fields (cannot use with include)
orderByobject | arraySort results
takenumberLimit number of results (max 100)
skipnumberOffset pagination (max 10000)
afternumber | stringCursor-based pagination (cannot use with skip)
_countbooleanReturn only the count of matching records

Where Operators

Filter conditions support the following operators:

OperatorDescriptionExample
equalsExact match{ price: { equals: 29.99 } }
notNot equal{ status: { not: 'archived' } }
inIn array{ status: { in: ['active', 'pending'] } }
notInNot in array{ status: { notIn: ['deleted'] } }
ltLess than{ price: { lt: 100 } }
lteLess than or equal{ price: { lte: 100 } }
gtGreater than{ price: { gt: 50 } }
gteGreater than or equal{ price: { gte: 50 } }
containsContains substring{ name: { contains: 'widget' } }
startsWithStarts with{ name: { startsWith: 'Premium' } }
endsWithEnds with{ name: { endsWith: 'Pro' } }

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

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

Query records with advanced filtering, sorting, and pagination.

:tableKeystringRequired
The table key or ID.
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 });
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

Response (findUnique)

{
  "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:

{
  "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

StatusErrorDescription
400Table key is requiredMissing table key in URL
400Record ID is requiredMissing record ID in URL
400data must be an objectInvalid data format in request body
400data cannot be emptyUpdate request with empty data object
400Query parameter is requiredMissing query in request body
400Invalid table key or ID formatTable key contains invalid characters
400take must be between 1 and 100Query take value out of range
400Either query.findMany or query.findUnique parameter is requiredMissing query type in request body
400Cannot specify both query.findMany and query.findUniqueBoth query types provided
400query.findUnique requires where.idMissing id in findUnique where clause
404Data table not foundTable doesn't exist or wrong app
404Data record not foundRecord doesn't exist
404Record not foundfindUnique query returned no results

Error Handling Example

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);
  }
}
Need help?
Can't find what you're looking for? Our team is here to help.