NAV

Knack Developer Docs

Introduction

Knack offers a comprehensive API that gives designers and developers full access to update and customize Knack applications. This documentation covers how you can:

  1. Use the Knack API to create, retrieve, update, and delete your apps' records.
  2. Use client-side JavaScript to trigger actions when your users perform specific events in your live applications.
  3. Use CSS to customize your application's design, including the layout, colors, and text.

Several examples of each are provided at the end of each section.

The API

The Knack API is organized around REST. Our API has predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. We use built-in HTTP features, like HTTP authentication and HTTP verbs, which are understood by off-the-shelf HTTP clients.

Our API follows most conventions of RESTful architecture. Any requests which require authentication must always be authenticated, as our servers do not retain information from previous requests. Our routes are reliably simple and lack more than a few levels of nesting.

All data sent to and from the API must be in JSON format. Many coding languages include utilities to convert data structures to JSON strings and vice versa.

We provide example API requests throughout the documentation. Each example is from the Contact Directory template app. You can add this app into your own account, but, note that you will need to replace the placeholder header values and record IDs in these requests in order to get them working with your own apps.

Using the API

The API is used to make requests to create, retrieve, update, or delete records that are stored in your Knack apps.

Requests are made for records from either a specific object in your app or a specific view on one of your pages.

Object-based requests require an API key and have full access to every field and record in your app.

View-based requests follow the rights and fields that were set up in the view, so no API key is required. For example, requesting records from a table view would only return fields for the columns that are included on that table.

All requests are authenticated via HTTP headers that need to be passed in with each request.

Note that you can make a maximum of 10 requests to the Knack API per second, while your plan will determine your daily API call limit. Read more about limits here.

Object-Based Requests

Object-based requests give you unrestricted access to create/retrieve/update/delete records in all fields on all records for a specific object.

Each object-based request requires an object key in its URL. You can find the object key for any object by selecting that object from left-hand menu in the Builder and checking your URL. The object key for the Invoices object in the following example is object_13:

Object keys

Every object-based request requires authentication with an API key.

When to Use Object-Based Requests

Authenticating Object-Based Requests

Object-based requests require at least two headers: an application ID - to identify to our servers the app whose records you are requesting - and an API key (which is specific to each app) to authenticate the request.

PUT and POST requests require that you tell our servers explicitly that your payload is in JSON format via the content-type header, whose value should always be application/json.

Request Headers

Key Value Required
X-Knack-Application-Id Your application ID Yes
X-Knack-REST-API-KEY Your API key Yes
content-type application/json Only for PUT and POST requests

View-Based Requests

View-based requests give you access to create/retrieve/update/delete records exactly as your apps' users do while interacting with your live apps. This means that:

Note that you do not need to authenticate these requests with an API key, making them a more secure option for being made from client-side code, such as your own apps' custom JavaScript.

These requests are often used in conjunction with JavaScript events to add advanced functionality to the behavior of live Knack apps.

All view-based requests are accessed through a page key and view key and use a URL in the following format:

https://api.knack.com/v1/pages/page_key/views/view_key/records

Finding page and view keys

You can find your page and view keys by going to the Builder and selecting the view through which you want to access your records and checking your URL, where you will see, for example, scene_1 and view_1:

Page and view keys

When to Use the View-Based API

Authenticating View-Based Requests

View-based requests require at least two headers: an application ID - to identify to our servers the app whose records you are requesting - and the value of knack for the API key header; note that you should not include your actual API key in view-based requests.

To authenticate requests to views on pages protected by logins, you will need to use the Authorization header whose value should be a user token.

Note that PUT and POST requests require that you tell our servers explicitly that your payload is in JSON format via the content-type header, whose value should always be application/json.

Request Headers

Key Value Required
X-Knack-Application-Id Your application ID Yes
X-Knack-REST-API-KEY knack Yes
Authorization A user token valid for the view Only for login-protected pages
content-type application/json Only for PUT and POST requests

Find Your API Key & Application ID

Both your Knack Application ID and API Key are available in your builder’s settings which can be found by clicking the name of your app in the upper-left corner, then Settings, then API & Code:

API key and application ID in Builder

Working with Fields

Records stored in your app are indexed by the field key (e.g. field_1) and include a unique record ID which you can use to specify that record in subsequent requests.

You can determine the field key of any field by selecting the field in the Builder and viewing the key at the end of the URL:

Finding your field keys

Field Types

The information and examples below demonstrate how each of Knack's field types is formatted in the JSON you will send and receive.

Short Text

Short text values are stored as strings.

"Knack"

Paragraph Text

Paragraph text values are stored as strings.

"Knack is the easiest way to build online database apps."

Number

Number fields are stored as numbers.

8

Yes/No

Yes/no fields store boolean values, though you can use either strings or booleans in your PUT and POST requests. Note that you can use "yes," "on," "false," or "off," as well, and that the API is not case-sensitive with these values.

true

Name

Name fields are stored as objects with a title, first, middle, and last name, each of which is a string.

{
  "title":"Mr.",
  "first":"Brandon",
  "middle":"R",
  "last":"Griggs"
}

Email

Email fields are stored as objects with an email and label, each of which is a string.

{
  "email":"support@knack.com",
  "label":"Knack Support"
}

Multiple Choice

Multiple choice fields come in two variants: one selection (string) and multiple selections (array of strings).

// One selection
"Large"

// Multiple selections
["Option 1", "Option 2"]

Date/Time

Date/time fields have four variants, each of which is an object with its own type of formatting: single date as object, single date as string, from-to date/all day, and time-only. See below for an example of each.

Note that the type of date format selected for the field in question (in the Builder) will determine the format in which you should enter the date. String dates will also be parsed and converted into the correct object.

Read more about these settings here.

// Single date as object:
  {
    "date":"02/08/2013",
    "hours":11,
    "minutes":47,
    "am_pm":"pm"
  }

// Single date as string
"03/28/2014 10:30pm"

// From-to date/all-day
{
  "date":"02/08/2013",
  "to":{
    "date":"02/11/2013"
  },
  "all_day":true
}

// Time-only
{
  "hours":2,
  "minutes":0,
  "am_pm":"pm"
}

Address

Address fields are stored as objects with string values for street, street2, city, state, zip code, and, if international, country.

The settings which determine this format are set in the Builder and explained in further detail here.

// US address
{
  "street":"123 Fake St",
  "street2":"Apt 2",
  "city":"Amonate",
  "state":"VA",
  "zip":"24601"
}

// International address with country
{
  "country": "United States",
  "zip": "24601",
  "state": "VA",
  "city": "Amonate",
  "street2": "Apt 2",
  "street": "123 Fake St., Apt 2"
}

Link fields are stored as objects with a URL and, if applicable, a label.

{
  "url":"https://www.knack.com",
  "label":"Knack"
}

Rich Text

Rich text fields are stored as strings.

"<h1>I'm a Title</h1><p>I'm some <strong>bold</strong> text</p>"

Currency

Currency fields are stored as numbers (note that the type of currency is therefore not included).

9.99

Auto Increment

Auto increment fields are stored as numbers. Note that auto increment fields are read-only.

1081

Rating

Rating fields are stored as numbers.

4.5

Timer

Timer fields are stored as an object with an array, "times", which consists of of an object for the "from" and "to" time, each.

{
  "times":[
    {
      "from":{
        "date":"02/09/2013",
        "hours":"04",
        "minutes":"45",
        "am_pm":"PM"
      },
      "to":{
        "date":"02/09/2013",
        "hours":"05",
        "minutes":"45",
        "am_pm":"PM"
      }
    }
  ]
}

Formula

Formulas - counts, sums, etc. - are stored as numbers. Note that formula fields are read-only.

25

Connection

Connections are stored as arrays of record IDs (strings), even when there is only one connected record. Each of these is the ID of a record of the object to which this field connects.

// More than one connected record
["4fea069e9e8246001d000698", "4fea069e9e8246001d000699"]

// One connected record
["4fea069e9e8246001d000699"]

Image

Image fields are stored as objects with their ID, application ID, file name, thumbnail URL, and other information. Read more about uploading images here.

{
  "id": "5579d9b1ca0f5a3f09efd93d",
  "application_id": "557786eacc034e367eb71e79",
  "s3": true,
  "type": "image",
  "filename": "logo.png",
  "url": "http://assets.knack.com/assets/5579d9b1ca0f5a3f09efd93d/original/logo.png",
  "thumb_url": "http://assets.knack.com/assets/5579d9b1ca0f5a3f09efd93d/thumb/logo.png",
  "size": 3588
}

File

Files are stored just like images. Read more about uploading files here.

{
  "id": "5579dde5ca0f5a3f09efd93f",
  "application_id": "557786eacc034e367eb71e79",
  "s3": true,
  "type": "file",
  "filename": "dates.csv",
  "url": "http://assets.knack.com/assets/5579dde5ca0f5a3f09efd93f/original/dates.csv",
  "thumb_url": "http://assets.knack.com/assets/5579dde5ca0f5a3f09efd93f/thumb/dates.csv",
  "size": 721
}

Response Format

Records stored in your app are indexed by the field key (e.g. field_1) and include a unique record ID which you can use to specify that record in subsequent requests. See below for a examples responses you might see for a GET request for multiple records as well as for any GET, POST, or PUT request which returns a single record.

Single Record

When retrieving a single record via a GET request, inserting a record via a POST request, or updating a record via a PUT request, the JSON body of a successful response will be the record in question as a JSON object.

Example Response (200 OK)

{
  "id": "58643557d1ea9432222f3cbb",
  "field_1": "Pearl Architectural Design",
  "field_1_raw": "Pearl Architectural Design",
  "field_17": "<a href=\"http://www.pearlinc.com\" target=\"_blank\">http://www.pearlinc.com</a>",
  "field_17_raw": {
    "url": "http://www.pearlinc.com"
  },
  "field_18": "Services",
  "field_18_raw": "Services",
  "field_31": "516-334-3077",
  "field_31_raw": "516-334-3077"
}

Multiple Records

When retrieving multiple records via a GET request, the body of your response will be a JSON object containing the following information:

Example Response (200 OK)

{
  "total_pages": 1,
  "current_page": 1,
  "total_records": 2,
  "records": [
    {
      "id": "58643557d1ea9432222f3cbb",
      "field_1": "Pearl Architectural Design",
      "field_1_raw": "Pearl Architectural Design",
      "field_17": "<a href=\"http://www.pearlinc.com\" target=\"_blank\">http://www.pearlinc.com</a>",
      "field_17_raw": {
        "url": "http://www.pearlinc.com"
      },
      "field_18": "Services",
      "field_18_raw": "Services",
      "field_31": "516-334-3077",
      "field_31_raw": "516-334-3077"
    },
    {
      "id": "58643556d1ea9432222f3ca6",
      "field_1": "Kelly and Cohen",
      "field_1_raw": "Kelly and Cohen",
      "field_17": "<a href=\"http://www.kellycohen.com\" target=\"_blank\">http://www.kellycohen.com</a>",
      "field_17_raw": {
        "url": "http://www.kellycohen.com"
      },
      "field_18": "Services",
      "field_18_raw": "Services",
      "field_31": "315-296-4146",
      "field_31_raw": "315-296-4146"
    }
  ]
}

Multiple Records: Response Data

Key Value
total_pages Total number of pages of results which match your filters (if any)
current_page The current page of the records returned by your request
total_records The total number of records which match your filters (if any)
records An array of JSON objects, where each object represents a record

The Knack API uses status codes in the 200 range to indicate success and status codes in the 400 range to indicate an error. Codes in the 500 range indicate a server-side error. Most errors can be fixed by checking the JSON formatting of your request’s body (there are plenty of great online JSON validators), your application ID, and (for object-based calls) your API key.

Code Request Type Message Notes
200 OK POST/PUT <Created/updated record (JSON), including ID>
200 OK GET <Record(s) queried>
200 OK DELETE { “delete”: true }
400 Bad Request Any Malformed App ID.
401 Unauthorized Any Invalid API key
401 Unauthorized Any Invalid API request Results from no application ID included among request headers
429 Too Many Requests Any Either "Rate limit exceeded" or "Plan Limit Exceeded". Read more here

API Limits

When you make many API requests within a short amount of time, you may receive a 429 Too Many Requests response back. When you reach the daily or rate limit, Knack will stop processing any more requests until a certain amount of time has passed.

The default daily limits for a Knack account are as follows:

Plan Daily Limit
Starter 1,000
Pro 5,000
Corporate 10,000
Enterprise 25,000

If you’d like to increase the number of requests you’re able to make in a day, please fill out the form located here.

For all plan types, there is a rate limit of 10 requests per second.

Which Requests Count Toward Limits

Catching 429 Errors

We strongly recommend that any code which interacts with our API catches any errors caused by hitting our rate limit, since your code can wait under a second for the limit to expire and then retry the failed request. Upon hitting the rate limit, the HTTP response which you’ll receive from Knack will have the HTTP status code 429 Too Many Requests with the message, “Rate limit exceeded”. Every request after this, until the one second is up, will continue to produce these errors and your requests will be ignored by Knack.

As an example, a request which goes over the rate limit might return the following response:

X-PlanLimit-Limit: 10000
X-PlanLimit-Remaining: 9888
X-PlanLimit-Reset: 10738500
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1000
Status Code: 429 Too Many Requests

This response contains some key information we can use to avoid hitting a rate limit:

With the above response information in mind, you would be able to alter your script to catch rate-limit errors and alter your next requests accordingly.

As an example, the following pseudo-code will catch the rate-limit error and retry:

response = request.get(url, headers)
rateLimitStart = 1000
retryAfter = rateLimitStart - response.x-ratelimit-remaining
if (response.status == 429)
{
  log("Rate limited, waiting " + retryAfter + " ms before trying again.")
  sleep(retryAfter)
  retry(url, headers, data)
}

Reducing the Amount of API Requests

To reduce the number of requests you make, we suggest:

Throttling the Rate of Requests

If you frequently hit your rate limit we strongly suggest throttling the rate at which your requests are being sent to Knack, as this is the most common reason someone would hit the limit. This can be done either statically or dynamically, and clues can be drawn from the aforementioned X-RateLimit-Remaining and X-PlanLimit-Remaining response headers. In order to avoid hitting the rate limit, you might:

Creating Records

You can create records through both the object-based and view-based APIs by sending a JSON payload of your record in a POST request to the appropriate route.

Constructing Your Payload

Object-Based POST

Object-based POST requests use the relevant object key in the URL along with the headers outlined below:

URL

POST https://api.knack.com/v1/objects/object_xx/records

Parameters

Parameter Explanation Example
object_xx object key object_1

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY Your API key
content-type application/json

Example Request

# Create a Company record
curl -X POST "https://api.knack.com/v1/objects/object_1/records" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: YOUR-API-KEY" \
  -H "Content-Type: application/json" \
  --data '{
        "field_1": "Name of new company",
        "field_16": {
          "street": "123 Fake St.",
          "city": "Amonate",
          "state": "VA",
          "zip": "24601"
        },
        "field_17": {
          "url": "https://example.com"
        },
        "field_18": "Other",
        "field_31": "555-555-5555"
      }'

Example Response (200 OK)

{
  "id": "58645233669adec2460888c4",
  "field_1": "Name of new company",
  "field_1_raw": "Name of new company",
  "field_16": "123 Fake St.<br />Amonate, VA 24601",
  "field_16_raw": {
    "zip": "24601",
    "state": "VA",
    "city": "Amonate",
    "street": "123 Fake St."
  },
  "field_17": "<a href=\"https://example.com\" target=\"_blank\">https://example.com</a>",
  "field_17_raw": {
    "url": "https://example.com"
  },
  "field_18": "Other",
  "field_18_raw": "Other",
  "field_31": "555-555-5555",
  "field_31_raw": "555-555-5555"
}

View-Based POST

To create a new record in Knack, send a POST request to the route for a form view, using its page and view keys:

Note that the only fields included in your payload should be the ones included in the form itself, as no others will be included.

Also keep in mind that all relevant record rules on the form in question will be triggered by a POST request to that form.

URL

POST https://api.knack.com/v1/pages/page_xx/views/view_yy/records

Parameters

Parameter Explanation Example
page_xx Page key page_3
view_yy View key view_5

Headers

Key Value
X-Knack-Application-Id Your application ID
Authorization A user token valid for the view (if view is nested under a login)
content-type application/json

Example Request

curl -X POST "https://api.knack.com/v1/pages/scene_13/views/view_12/records" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: knack" \
  -H "Content-Type: application/json" \
  --data '{
        "field_1": "Name of new company",
        "field_16": {
          "street": "123 Fake St.",
          "city": "Amonate",
          "state": "VA",
          "zip": "24601"
        },
        "field_17": {
          "url": "https://example.com"
        },
        "field_18": "Other",
        "field_31": "555-555-5555"
      }'

Example Response (200 OK)

{
  "record": {
    "id": "5865077510a48064455804a7",
    "field_1": "Name of new company",
    "field_1_raw": "Name of new company",
    "field_16": "123 Fake St.<br />Amonate, VA 24601",
    "field_16_raw": {
      "zip": "24601",
      "state": "VA",
      "city": "Amonate",
      "street": "123 Fake St."
    },
    "field_17": "<a href=\"https://example.com\" target=\"_blank\">https://example.com</a>",
    "field_17_raw": {
      "url": "https://example.com"
    },
    "field_18": "Other",
    "field_18_raw": "Other",
    "field_31": "555-555-5555",
    "field_31_raw": "555-555-5555"
  },
  "submit_key": "submit_1"
}

Retrieving Records

You can retrieve your records by making a GET request to the appropriate route as outlined below. You can either query for up to 1,000 records at once - including with field-specific filters - or retrieve a specific record by its ID.

Retrieving Multiple Records

You can retrieve multiple records at once - just like the Builder and your live apps' table and list views do - by making a GET request to the appropriate object- or view-based URL.

Retrieving Multiple Records: Object-Based GET

Simply make a GET request to a URL like the one below, specifying the object by its key:

URL

GET https://api.knack.com/v1/objects/object_xx/records

Parameters

Parameter Explanation Example
object_xx object key object_1

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY Your API key

Example Request

curl -X GET "https://api.knack.com/v1/objects/object_1/records" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: YOUR-API-KEY"

Example Response (200 OK)

Note that this is abbreviated - only two records are included out of the response's fourteen.

Read more about how to interpret this information here.

{
  "total_pages": 1,
  "current_page": 1,
  "total_records": 14,
  "records": [
    {
      "id": "58643557d1ea9432222f3cc0",
      "field_1": "Dodgit",
      "field_1_raw": "Dodgit",
      "field_16": "334 Menlo Drive<br />Menlo, CA 84432",
      "field_16_raw": {
        "longitude": -111.97383,
        "latitude": 41.223,
        "street": "334 Menlo Drive",
        "street2": "",
        "city": "Menlo",
        "state": "CA",
        "zip": "84432"
      },
      "field_17": "<a href=\"http://www.dodgit.com\" target=\"_blank\">http://www.dodgit.com</a>",
      "field_17_raw": {
        "url": "http://www.dodgit.com"
      },
      "field_18": "Entertainment",
      "field_18_raw": "Entertainment",
      "field_31": "717-443-5567",
      "field_31_raw": "717-443-5567"
    },
    {
      "id": "58643557d1ea9432222f3cbe",
      "field_1": "Ejecta",
      "field_1_raw": "Ejecta",
      "field_16": "689 Marshville Road<br />Spring Valley, NY 10977",
      "field_16_raw": {
        "street": "689 Marshville Road",
        "city": "Spring Valley",
        "state": "NY",
        "zip": "10977",
        "latitude": 41.130548,
        "longitude": -74.051227
      },
      "field_17": "<a href=\"http://www.ejecta.com\" target=\"_blank\">http://www.ejecta.com</a>",
      "field_17_raw": {
        "url": "http://www.ejecta.com"
      },
      "field_18": "Tech",
      "field_18_raw": "Tech",
      "field_31": "845-579-3734",
      "field_31_raw": "845-579-3734"
    },
    ..........
  ]
}

Retrieving Multiple Records: View-Based GET

You can retrieve multiple records by sending a GET request to a view’s route. The following views can display multiple records:

Note that the only fields included inthe response will be those included in the view specified in your URL, and that the view's data source will impact the records which your requests retrieve.

URL

GET https://api.knack.com/v1/pages/page_xx/views/view_yy/records

Parameters

Parameter Explanation Example
page_xx Page key page_3
view_yy View key view_5

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY knack
Authorization A user token valid for the view (if view is nested under a login)

Example Request

curl -X GET "https://api.knack.com/v1/pages/scene_1/views/view_11/records" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: knack"

Example Response (200 OK)

Note that this is abbreviated - only two records are included out of the response's fourteen.

Also note that are fields included here which do not appear in the response to our object-based GET requests; this is because the table used in this example - the Companies table - includes a column for Contacts.

{
  "total_pages": 1,
  "current_page": 1,
  "total_records": 15,
  "records": [
    {
      "id": "58643557d1ea9432222f3cb5",
      "field_1": "BASCO",
      "field_1_raw": "BASCO",
      "field_16": "4295 Drainer Avenue<br />Tallahassee, FL 32304",
      "field_16_raw": {
        "longitude": -84.177903,
        "latitude": 30.463511,
        "zip": "32304",
        "state": "FL",
        "city": "Tallahassee",
        "street2": "",
        "street": "4295 Drainer Avenue"
      },
      "field_17": "<a href=\"http://www.basco.com\" target=\"_blank\">http://www.basco.com</a>",
      "field_17_raw": {
        "url": "http://www.basco.com"
      },
      "field_18": "Entertainment",
      "field_18_raw": "Entertainment",
      "field_14_raw": [
        {
          "first": "Barbara",
          "last": "Waters"
        },
        {
          "first": "Josefa",
          "last": "Willems"
        }
      ],
      "field_14": "<a href=\"#58643557d1ea9432222f3cd1\"><span id=\"58643557d1ea9432222f3cd1\">Barbara Waters</span></a><br /><a href=\"#58643558d1ea9432222f3ce9\"><span id=\"58643558d1ea9432222f3ce9\">Josefa Willems</span></a>"
    },
    {
      "id": "58643557d1ea9432222f3cb1",
      "field_1": "Cavages",
      "field_1_raw": "Cavages",
      "field_16": "3885 Mount Street<br />Saginaw, MI 48607",
      "field_16_raw": {
        "street": "3885 Mount Street",
        "city": "Saginaw",
        "state": "MI",
        "zip": "48607",
        "latitude": 44.352592,
        "longitude": -84.419193
      },
      "field_17": "<a href=\"http://www.cavages.com\" target=\"_blank\">http://www.cavages.com</a>",
      "field_17_raw": {
        "url": "http://www.cavages.com"
      },
      "field_18": "Health Care",
      "field_18_raw": "Health Care",
      "field_14_raw": [
        {
          "first": "Daniel",
          "last": "Navarrete"
        },
        {
          "first": "Mark",
          "last": "Silverstein"
        }
      ],
      "field_14": "<a href=\"#58643557d1ea9432222f3ccc\"><span id=\"58643557d1ea9432222f3ccc\">Daniel Navarrete</span></a><br /><a href=\"#58643558d1ea9432222f3cd9\"><span id=\"58643558d1ea9432222f3cd9\">Mark Silverstein</span></a>"
    },
    ..........

Retrieving One Record

You can retrieve a specific record by making a GET request with the record's ID appended to an appropriate view- or object-based base URL.

Retrieving One Record: Object-Based GET

URL

GET https://api.knack.com/v1/objects/object_xx/records/record_ID

Parameters

Parameter Explanation Example
object_xx object key object_1
record_ID A record’s ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY Your API key

Example Request

curl -X GET "https://api.knack.com/v1/objects/object_1/records/58643557d1ea9432222f3cbb" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: YOUR-API-KEY"

Example Response (200 OK)

{
  "id": "58643557d1ea9432222f3cbb",
  "field_1": "Pearl Architectural Design",
  "field_1_raw": "Pearl Architectural Design",
  "field_16": "4913 Gnatty Creek Road
Westbury, NY 11590", "field_16_raw": { "street": "4913 Gnatty Creek Road", "city": "Westbury", "state": "NY", "zip": "11590", "latitude": 40.89964, "longitude": -73.601586 }, "field_17": "<a href=\"http://www.pearlinc.com\" target=\"_blank\">http://www.pearlinc.com</a>", "field_17_raw": { "url": "http://www.pearlinc.com" }, "field_18": "Services", "field_18_raw": "Services", "field_31": "516-334-3077", "field_31_raw": "516-334-3077" }

Retrieving One Record: View-Based GET

Once you’ve created a record, you can retrieve it by making a GET request to the route of any view which displays it and appending the ID to that route. These views include:

Note that the only fields included inthe response will be those included in the view specified in your URL, and that the view's data source will impact the records which your requests retrieve.

URL

GET https://api.knack.com/v1/pages/page_xx/views/view_yy/records/record_ID

Parameters

Parameter Explanation Example
page_xx Page key page_3
view_yy View key view_5
record_ID A record’s ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY knack
Authorization A user token valid for the view (if view is nested under a login)

Example Request

curl -X GET "https://api.knack.com/v1/pages/scene_10/views/view_9/records/58643557d1ea9432222f3cb5" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: knack"

Example Response (200 OK)

{
  "id": "58643557d1ea9432222f3cb5",
  "field_1": "BASCO",
  "field_1_raw": "BASCO",
  "field_16": "4295 Drainer Avenue<br />Tallahassee, FL 32304",
  "field_16_raw": {
    "longitude": -84.177903,
    "latitude": 30.463511,
    "zip": "32304",
    "state": "FL",
    "city": "Tallahassee",
    "street2": "",
    "street": "4295 Drainer Avenue"
  },
  "field_18": "Entertainment",
  "field_18_raw": "Entertainment",
  "field_17": "<a href=\"http://www.basco.com\" target=\"_blank\">http://www.basco.com</a>",
  "field_17_raw": {
    "url": "http://www.basco.com"
  },
  "field_31": "850-644-1128",
  "field_31_raw": "850-644-1128"
}

Filters

Filters are used to search and limit the records returned by GET requests for multiple records. Filtered API requests return the same results as a filtered table in your live app.

Just like in the Builder and live apps, you can specify that the records retrieved match either all or any of your filters.

Constructing Filters

Begin by adding a filters parameter to your URL. Its value should be a JSON object with two keys: match and filters.

The value for match should be either "and" or "or".

The value for the rules key must be a JSON array of objects, where each object is a filter. Most filters consist of three key-value pairs:

  1. field: the key of the field you want to filter by - e.g. “field_1”
  2. operator: the operator for the filter, like “is” or “contains”
  3. value: the value that the operator uses to filter by

Note that filters which do not require values through the Builder or front end do not require them in API calls (e.g., “is blank,” “is not blank,” etc.)

Let's take a look at a JavaScript example from the Contact Directory template app, where we specify that a company's name must be either "Dodgit" or blank.

// Request route
var api_url = 'https://api.knack.com/v1/objects/object_1/records';

// Prepare filters
var filters = {
  'match': 'or',
  'rules': [
             {
               'field':'field_1',
               'operator':'is',
               'value':'Dodgit'
             },
             {
               'field':'field_1',
               'operator':'is blank'
             }
           ]
};

// Add filters to route
api_url += '?filters=' + encodeURIComponent(JSON.stringify(filters));

Note that filters for date ranges have two additional variables: range and type:

var api_url = 'https://api.knack.com/v1/objects/object_1/records';

// field_3 is a date/time field
var filters = [
  // Filter for records with a value for this field in the last three months
  {
    "field":"field_3",
    "operator":"is+during+the+previous"
    "range":3,
    "type":"months"
  }
];

// Add filters to route
api_url += '?filters=' + encodeURIComponent(JSON.stringify(filters));

Filters and Field Types

Below is a list of Knack's field types along with your options for the operator value for each.

Field Type Filters permitted
Short Text contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Paragraph Text contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Yes/No is, is not, is blank, is not blank
Multiple Choice is, is not, contains, does not contain, is any, is blank, is not blank
Date/Time (single date) is, is not, is during the current, is during the previous, is during the next, is before the previous, is after the next, is before, is after, is today, is today or before, is today or after, is before today, is after today, is before current time, is after current time, is blank, is not blank
Number is, is not, higher than, lower than, is blank, is not blank
Image is blank, is not blank
File is blank, is not blank
Address contains, does not contain, is, is not, starts with, ends with, is blank, is not blank, near
Name contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Link is, is not, is blank, is not blank
Email contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Phone contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Rich Text contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Currency contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Auto Increment contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Timer is, is not, higher than, lower than, is blank, is not blank
Rating is, is not, higher than, lower than, is blank, is not blank
Text Formula contains, does not contain, is, is not, starts with, ends with, is blank, is not blank
Signature is blank, is not blank

Filtering by a Connected Record

The example which follows along with the steps below uses two objects: Employees and Companies, where many Employees connect with one Company. The connection field is on the employee, and it is called Employed By.

The example explains how to query for Employees connected to a specific company, Knack.

  1. Identify the parent record by which you want to filter a request for child records. Make a request for this record by querying the parent object with a filter for the record’s Display (or any other unique) field.
    • Example: Identify the Company record by which you want to filter a request for Employees. Make a request for this record by querying the Company object with a filter for the record’s Company Name, for instance.
  2. Locate the Connection field, which is usually on the child object. Select it in your Builder and note the field key at the end of your URL.
    • Example: Employed By field on the Employee Object. Let’s say the its field key is field_12.
  3. Add your filter using the Connection field, the is operator, and the parent record’s ID for a value.
    • Example: {“field”:“field_12”, “operator”:“is”, “value”:“574ec622dadf69d041755605”}

A complete object-based request route, where Company is object_1, would look like this:

https://api.knack.com/v1/objects/object_1/records?filters=[{"field":"field_12", "operator":"is", "value":"574ec622dadf69d041755605"}]

Example

Formatting, Sorting, & Pagination

Knack offers additional parameters to help format, sort, and paginate the records you retrieve with GET requests.

Formatting

You can choose any of the three data formats listed below by appending the format parameter with your choice, to your request's URL.

Notes

Examples

https://api.knack.com/v1/objects/object_1/records?format=raw

https://api.knack.com/v1/objects/object_1/records?format=html

https://api.knack.com/v1/pages/scene_1/views/view_1/records?format=both

Sorting

You can indicate sorting by appending with the following parameters:

Examples

https://api.knack.com/v1/objects/object_1/records?sort_field=field_25&sort_order=asc

https://api.knack.com/v1/objects/object_1/records?sort_field=field_25&sort_order=desc

Pagination

The API can return up to 1,000 records at a time, meaning that retrieving more records will require multiple requests, where each would specify which page - or set of (up to 1,000) records - to retrieve.

Examples

https://api.knack.com/v1/objects/object_1/records?page=2&rows_per_page=25

Because this example requests page 2 with 25 records per page, it will retrieve the 26th through 50th records.

https://api.knack.com/v1/objects/object_1/records?page=3&rows_per_page=100

Because this example requests page 3 with 100 records per page, it will retrieve the 201st through 300th records.

Updating Records

You can update an individual record by making a PUT request to the relevant base URL with a record ID appended.

Note that you will need to construct a JSON payload just like the one you would need for a POST request, where the only difference is that only fields included in your payload will be updated; all others will remain unchanged.

Object-Based PUT

The URL should include the object key and a record ID, while the headers will include one for content-type as well as those for authentication.

URL

PUT https://api.knack.com/v1/objects/object_xx/records/record_ID

Parameters

Parameter Explanation Example
object_xx object key object_1
record_ID A record’s ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY Your API key
content-type application/json

Example Request

# Update a Company record
curl -X PUT "https://api.knack.com/v1/objects/object_1/records/58643557d1ea9432222f3cbb" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: YOUR-API-KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "field_1":"Updated name",
        "field_18":"Other"
      }'

Example Response (200 OK)

{
  "id": "58643557d1ea9432222f3cbb",
  "field_1": "Updated name",
  "field_1_raw": "Updated name",
  "field_16": "4913 Gnatty Creek Road
Westbury, NY 11590", "field_16_raw": { "street": "4913 Gnatty Creek Road", "city": "Westbury", "state": "NY", "zip": "11590", "latitude": 40.89964, "longitude": -73.601586 }, "field_17": "<a href=\"http://www.pearlinc.com\" target=\"_blank\">http://www.pearlinc.com</a>", "field_17_raw": { "url": "http://www.pearlinc.com" }, "field_18": "Other", "field_18_raw": "Other", "field_31": "516-334-3077", "field_31_raw": "516-334-3077"
}

View-Based PUT

To change the data stored for a record which already exists, send a PUT request to the URL for an edit form with the record key appended.

Note that the only fields included inyour payload should be the ones included in the form itself, as no others will be updated.

Also keep in mind that all relevant record rules on the form in question will be triggered by a PUT request to that form.

URL

PUT https://api.knack.com/v1/pages/page_xx/views/view_yy/records/record_ID

Parameters

Parameter Explanation Example
page_xx Page key page_3
view_yy View key view_5
record_ID A record’s ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY knack
Authorization A user token valid for the view (if view is nested under a login)
content-type application/json

Example Request

curl -X PUT "https://api.knack.com/v1/pages/scene_19/views/view_18/records/586504ff070097a1461db2aes" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: knack" \
  -H "Content-Type: application/json" \
  --data '{
        "field_1": "New name of new company"
      }'

Example Response (200 OK)

{
  "record": {
    "id": "586504ff070097a1461db2ae",
    "field_1": "New name of new company",
    "field_1_raw": "New name of new company",
    "field_16": "",
    "field_17": "",
    "field_18": "Other",
    "field_18_raw": "Other",
    "field_31": ""
  },
  "submit_key": "submit_1"
}

Deleting Records

You can delete a single record by making a DELETE request to the relevant base URL with the record's ID appended.

Object-Based DELETE

URL

DELETE https://api.knack.com/v1/objects/object_xx/records/record_ID

Parameters

Parameter Explanation Example
object_xx object key object_1
record_ID Knack record ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY Your API key

Example Request

curl -X DELETE "https://api.knack.com/v1/objects/object_1/records/58645233669adec2460888c4"
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: YOUR-API-KEY"

Example Response (200 OK)

{
  "delete": true
}

View-Based DELETE

To delete a record from an object, send a DELETE request to any view which contains a delete link. Views that can accept a delete request include the following:

URL

DELETE https://api.knack.com/v1/pages/page_xx/views/view_yy/records/record_ID

Parameters

Parameter Explanation Example
page_xx Page key page_3
view_yy View key view_5
record_ID A record’s ID 575482d691e16d4235adcdb6

Headers

Key Value
X-Knack-Application-Id Your application ID
X-Knack-REST-API-KEY knack
Authorization A user token valid for the view (if view is nested under a login)

Example Request

# NOTE: In order for this request to work, you will need to add a "delete" link to the View Company Details view (on the Company Details page)
curl -X DELETE "https://api.knack.com/v1/pages/scene_10/views/view_9/records/586504ff070097a1461db2ae" \
  -H "X-Knack-Application-Id: YOUR-APP-ID" \
  -H "X-Knack-REST-API-Key: knack"

Example Response (200 OK)

{
  "delete": true
}

File/Image Uploads

Uploading a file or image via the Knack API is a two step process. The first will perform an HTTP POST request with a multipart/form-data content type to the following URL: https://api.knack.com/v1/applications/app_id/assets/file/upload

1. Uploading the File

The following is a cURL request which uploads a file (replace “file” with “image” when uploading an image file):

curl -X POST "https://api.knack.com/v1/applications/YOUR-APP-ID/assets/file/upload" \
  -H 'content-type: multipart/form-data' \
  -H 'x-knack-rest-api-key: YOUR-API-KEY' \
  -F "files=@/path/to/your/file.txt"

A successful POST will return the following JSON response representing details about the uploaded file:

{
  "id": "YOUR-FILE-ID",
  "type": "file",
  "filename": "file.txt",
  "public_url": "https://api.knack.com/v1/applications/YOUR-APP-ID/download/asset/YOUR-FILE-ID/file.txt",
  "thumb_url": "",
  "size": 12043
}

2. Creating the Record

This response contains an id property that you will use in the next step. The public_url property will contain a direct link to the file itself, and the thumb_url property will contain a link to a thumbnail if an image was uploaded.

Use the file_id from the JSON response to make a second POST request to create a record referencing your file. Set that file_id to your image or file field.

The following is a cURL example, assuming field_1 is a file or image field:

curl -X POST "https://api.knack.com/v1/objects/object_xx/records" \
  -H "x-knack-rest-api-key: YOUR-API-KEY" \
	-H "x-knack-application-id: YOUR-APP-ID" \
	-H "content-type: application/json" \
	--data '{"field_11": "YOUR-FILE-ID"}'

Users, Sessions, & Remote Logins

This section covers how to authenticate view-based requests as a logged-in user so as to make requests to pages which are protected by logins.

User Tokens

User tokens are secure keys which are generated when a user logs in. By using a token as the value for the Authorization header in a view-based request, you can make secure, cross-origin requests as a logged-in user without exposing an API key.

Because the token identifies the specific user who retrieved it by logging in, token-authenticated requests behave just like a user's visit to your live Knack app; the authenticated user can only make requests to views to which he/she has access, and any forms whose record rules track the logged-in user will identify the user by this token. Similarly, requests to views which display records connected to the logged-in user will only return those same records.

Tokens are valid for two weeks after which they are replaced with new ones when the user logs in again.

After the user logs in, his/her token is accessible by calling Knack.getUserToken() - in your app's custom JavaScript - which returns the token as a string:

Knack.getUserToken();
//"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTU0N2VhNjVkMmE3MzgxNDA5MjU3YWQzIiwiYXBwbGljYXRpb25faWQiOiI1MzllNjYzYjZlNTNlY2M1NjAwMDBiNzkiLCJpYXQiOjE0MzA3NzY0NDF9.g7C3iJexG3Na2dt6m_vumheXLUe1H_4NEYKziH9Gtm8"

Note: You may notice that this header is not prefixed by “X-Knack-”. That’s because the authorization header for use in a token-based authentication systems is an industry-standard approach as defined by this Internet Engineering Task Force proposal.

See below for an example of using jQuery in a Knack app to determine a user’s token and then to make an API call using it. Note that there is no API key included in the headers.

// change "page_1" to the page you want to listen for
$(document).on('knack-page-render.page_1', function(event, page) {
  // change page_1 and view_1 to the view that works with the logged-in user
  $.ajax({
    url: 'https://api.knack.com/v1/pages/page_1/views/view_1/records',
    type: 'GET',
    headers: {
      'Authorization': Knack.getUserToken(),
      'X-Knack-Application-Id': Knack.application_id,
      'Content-Type': 'application/json'
    },
    success: function(data) {
      alert('Got records!')
      console.log(data);
    }
  });
});

Remote User Logins

If you want to make view-based API calls as a logged-in user from your server-side code, where you won't be able to use our JavaScript utility functions to get a user's token, you can log that user in remotely with a POST request to the following route with the user's email address and password included in the JSON payload:

https://api.knack.com/v1/applications/<app_id>/session

A full remote login request would look like the following (be sure to change YOUR-APP-ID to your application ID):

curl -H "Content-Type: application/json" \
     --data '{"email":"email address","password":"password"}' \
     "https://api.knack.com/v1/applications/YOUR-APP-ID/session"

Your response will look like the following, where you will want to store the value of token in a variable to use to authenticate your view-based requests.

.
// Data returned by remote login request
{
  "session": {
    "user": {
      "approval_status": "approved",
      "empty_pass": false,
      "id": "user_id",
      "profile_keys": [
        "profile_4"
      ],
      "profile_objects": [
        {
          "entry_id": "entry_id",
          "object": "object_4"
        }
      ],
      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTUzMWExNmI2YTEzMTk1ZTcyNmUxZTBmIiwiYXBwbGljYXYpb25faWQiOiI1NTIzMTM5ZjNiY2U2NDdhMDJiZDUxNTAiLCJpYXQiOjE0Mjk3MTgzNzl9.K-WULiJxT-st8vxnTkqxCtp2nv5ykrSToPQsTRx6r1I",
      "utility_key": "key",
      "values": {}
    }
  }
}

Using the token we got from the response to our remote login request, we can now authenticate our view-based requests like so:

# Sample authenticated request
curl -H "Content-Type: application/json"
     -H "X-Knack-Application-Id: YOUR-APP-ID"
     -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTUzMWExNmI2YTEzMTk1ZTcyNmUxZTBmIiwiYXBwbGljYXYpb25faWQiOiI1NTIzMTM5ZjNiY2U2NDdhMDJiZDUxNTAiLCJpYXQiOjE0Mjk3MTgzNzl9.K-WULiJxT-st8vxnTkqxCtp2nv5ykrSToPQsTRx6r1I"
     https://api.knack.com/v1/pages/page_1/views/view_1/records

API: Code Examples

Read on for some examples for how to use our API from your Knack app's JavaScript in order to work with connected records, make API calls after form submissions, and to add other functionality to your apps.

Note: This section is currently under construction - more examples coming soon!

Filters: Retrieve child records of a specific parent record

Using the Contact Directory template app as an example, let's get all of the Contact records which belong to the Company "BASCO".

// Set up headers for object-based requests
var appId = 'YOUR-APP-ID';
var apiKey = 'YOUR-API-KEY';

// Prep request for the Company with the name "BASCO"
var companyRequestUrl = 'https://api.knack.com/v1/objects/object_1/records';
var companyRequestFilters = [
  {
    'field':'field_1',
    'operator':'is',
    'value':'BASCO'
  }
];
var fullcompanyRequestUrl = companyRequestUrl + '?filters=' + encodeURIComponent(JSON.stringify(companyRequestFilters));

// Retrieve aforementioned Company
Knack.showSpinner();
$.ajax({
  url: fullcompanyRequestUrl,
  type: 'GET',
  headers: {'X-Knack-Application-Id': appId, 'X-Knack-REST-API-Key': apiKey}
}).done(function(data) {
  // Make sure we have only the one Company record we intended to get
  if (data.records.length !== 1) {
    Knack.hideSpinner();
    throw new Error('Retrieved more than one record - try adding more filters!');
    console.log(data.records);
  }

  // Now we can be certain that the first record - the only one - is the one we want
  // Set up another AJAX request - this time for Contact records where the Company is BASCO
  var companyRecordId = data.records[0].id;
  contactsRequestUrl = 'https://api.knack.com/v1/objects/object_2/records';
  contactsRequestFilters = [
    {
      'field':'field_19', // Company connection field on Contact object
      'operator':'is',
      'value':[companyRecordId]
    }
  ];
  fullcontactsRequestUrl = contactsRequestUrl + '?filters=' + encodeURIComponent(JSON.stringify(contactsRequestFilters));

  // Get all Contacts connected to BASCO
  $.ajax({
    url: fullcontactsRequestUrl,
    type: 'GET',
    headers: {'X-Knack-Application-Id': appId, "X-Knack-REST-API-Key": apiKey}
  }).done(function(data) {
    alert('Got the contacts! See browser\'s console for details');
    console.log(data);
    Knack.hideSpinner();
  })
});

Get all records for an object (pagination)

We can use the page URL parameter to specify which page of results we want, while we can minimize the number of records included per page with the rows_per_page URL parameter. Read more about these here.

In this example, we use recursion to retrieve each subsequent page of records. Because we are making these API calls asynchronously, we handle the results with a callback function.

// This function covers whatever we want to do with our data once it's all loaded
function handleData(allRecordsForObject) {
  console.log(allRecordsForObject);
}

function getAllRecordsForObject(pageKey, viewKey, callbackFunctionToHandleData, currentPage = 1, totalPages, allRecordsForObject = []) {
  if (!pageKey || !viewKey) {
    throw new Error('Missing page or view key!');
  }

  if (!callbackFunctionToHandleData) {
    throw new Error('No callback function provided; make sure the data is handled somehow.');
  }

  // AJAX prep
  var url = 'https://api.knack.com/v1/pages/' + pageKey + '/views/' + viewKey + '/records?page=' + currentPage + '&rows_per_page=500';
  var headers = {
    'X-Knack-Application-ID': 'YOUR-APP-ID',
    'X-Knack-REST-API-Key': 'knack'
  };

  $.ajax({
    url: url,
    headers: headers,
    type: 'GET',
  }).done(function(responseData) {
    allRecordsForObject = allRecordsForObject.concat(responseData.records);
    // Recursively continue getting records until we have all of them
    if (currentPage < responseData.total_pages) {
      currentPage++;
      getAllRecordsForObject(pageKey, viewKey, callbackFunctionToHandleData, currentPage, responseData.total_pages, allRecordsForObject);
      return;
    }
    // Handle ALL records with our callback function
    callbackFunctionToHandleData(allRecordsForObject);
  });
}

// When a certain view is rendered, retrieve all records for this object
// and handle it as specified by the handleData function
$(document).on('knack-view-render.view_299', function(event, view) {
  getAllRecordsForObject('scene_360', 'view_541', handleData);
});

Store records' Knack IDs in a field

You may want to store your records' internal, unique Knack IDs in a field to reference outside of your code. For this example, you will need the following:

  1. An object with a short text field to be used to store each record's ID (example below: field_2)
  2. A form to add records for that object (example below: view_1)
  3. A page with a form to edit records for that object (example below: scene_2/view_3). This form must contain an input for the short text field you use to store the ID.
// Listen for a record being created on view_1
$(document).on('knack-record-create.view_1', function(event, view, record) {
  // Set up PUT request to update the record
  // Note that we use the URL for a separate edit form which has an input for the ID field
  var url = 'https://api.knack.com/v1/pages/scene_2/views/view_3/records/' + record.id;
  var headers = {
    'X-Knack-Application-ID': Knack.application_id,
    'X-Knack-REST-API-Key': 'knack',
    'content-type': 'application/json'
  };
  // Set our short text field's value to the newly created record's Knack ID
  var data = { field_2: record.id }

  Knack.showSpinner();
  // Make the AJAX call
  $.ajax({
    url: url,
    type: 'PUT',
    headers: headers,
    data: JSON.stringify(data),
  }).done(function(responseData) {
    console.log('Record updated!');
    Knack.hideSpinner();
  });
});

Trigger a record insert after form submission

This example inserts a record connected to one which was just created via a form submission by using the record's ID - in an array - as a value for a new record's connection field.

// change the view key to the key of your form that is inserting the record
$(document).on('knack-record-create.view_273', function(event, view, record) {
  Knack.showSpinner();
  // Replace the page and view keys with your own
  var url = 'https://api.knack.com/v1/pages/page_1/views/view_2/records/';
  var headers = {
    'X-Knack-Application-ID': 'YOUR-APP-ID',
    'X-Knack-REST-API-Key':'knack',
    'content-type':'application/json'
  };
  // use data from inserted record
  var data = {
    field_1: [record.id], // connection field
    field_2: record.field_3
  }
  // insert the record
  $.ajax({
    url: url,
    type: 'POST',
    headers: headers,
    data: data,
    success: function(response) {
      alert('Record Added!');
      Knack.hideSpinner();
    }
  });
});

Looping Over and Inserting Records

The following are examples from our Attendance Manager example app. These functions insert Attendance records for each Student connected to the Teacher’s class. So instead of having to manually insert a new attendance record for each student, the Teacher can use inline editing instead to mark any students as absent.

var app_id = Knack.app.id;
var redirect = function (total, Class, attendance_Date) {if(total < 1) {Knack.hideSpinner(); window.location.replace('#classes3/classdetails/' + Class + '/classdaydetails/' + attendance_Date)} };

$(document).on('knack-record-create.view_149', function (event, view, record) {
  var user = Knack.getUserToken();
  var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}

  var attendance_date = record.id;
  var class = Knack.models['view_34'].toJSON().id;
  var students = Knack.models['view_34'].toJSON().field_28_raw;
  var total = Students.length;

  students.forEach(function (student) {

    Knack.showSpinner();

    var data = { field_72: attendance_date, field_65: student.id };
    $.ajax({
      url: 'https://api.knack.com/v1/pages/page_72/views/view_145/records/',
      type: 'POST',
      headers: headers,
      data: JSON.stringify(data),
      success: function (response) {
        console.log('Attendance added!!!');
        total--;
        redirect(total, class, attendance_date);
      }
    });
  });
});

$(document).on('knack-record-create.view_141', function (event, view, record) {
    var Class = $('#view_141-field_71').val();
    var attendance_Date = record.id;

    var user = Knack.getUserToken();
    var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}

  $.ajax({
    url: 'https://api.knack.com/v1/pages/page_1/views/view_25/records/' + Class,
    type: 'GET',
    headers: headers,
    success: function (data) {
        Knack.showSpinner();
        var students = data.field_28_raw;
        var total = Students.length;

        students.forEach(function (student) {
            var data = { field_72: attendance_Date, field_31: Class, field_65: student.id };
            $.ajax({
                url: 'https://api.knack.com/v1/pages/page_72/views/view_145/records/',
                type: 'POST',
                headers: headers,
                data: JSON.stringify(data),
                success: function (response) {
                    total--
                    console.log('Attendance added!!!');
                    redirect(total, class, attendance_date);
                }
           })
        });
    }
  });
});

$(document).on('knack-record-create.view_113', function (event, view, record) {
  var class_id = Knack.models['view_92'].toJSON().id;
  var attendance_date = record.id;
  var students = Knack.models['view_92'].toJSON().field_28_raw;
  var total = students.length;

  var user = Knack.getUserToken();
  var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}


  Students.forEach(function (student) {
    Knack.showSpinner();
    var data = { field_72: attendance_date, field_65: student.id };
    $.ajax({
      url: 'https://api.knack.com/v1/pages/page_72/views/view_145/records/',
      type: 'POST',
      headers: headers,
      data: JSON.stringify(data),
      success: function (response) {
        total--
        if(total < 1) {
          Knack.hideSpinner();
          window.location.replace('#classes/viewclassdetails/' + class_id + '/classattendance/' + attendance_date);
        }
      }
    });
  });
});

$(document).on('knack-record-create.view_73', function (event, view, record) {
    var meeting = record.id;
    var teachers = record.field_37_raw;

    var user = Knack.getUserToken();
    var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}


    teachers.forEach(function (teacher) {
      var data = {field_75:teacher.id, field_74: meeting};
        $.ajax({
          url:'https://api.knack.com/v1/pages/page_74/views/view_147/records',
          type: 'POST',
          headers: headers,
          data: JSON.stringify(data),
          success: function (response) {
            console.log('Record Added!');
          }
        });
  })
});

jQuery & JavaScript

Knack apps generate JavaScript events for form submissions, view renders, page renders, and more. This means you can add custom JavaScript handlers to subscribe to these events. Whenever an event to which you've subscribed is triggered, your handler will then be called.

These handlers can contain custom code to extend your app beyond the functionality included in the Knack platform. For example, you could respond to a form submission event to insert that submitted data into a separate CRM or into your own database through a server whose code you maintain.

How to Add Custom Code

You can add JavaScript directly to your app by hovering your mouse over the Settings icon (upper-right)...

API and code in settings menu

...and then API & Code, and then JavaScript at the top:

Getting started

Interface Events

Code inside these event listeners will be executed whenever a page or view - depending on your listener - is loaded. They are used most often either for manipulating the HTML/CSS of the view to make visual changes or to make requests to Knack's or other external APIs for specific pages/views.

Page Render

// Change "page_1" to the page you want to listen for
$(document).on('knack-page-render.page_1', function(event, page) {
  // Do something after the page renders
  alert('listener for page: ' + page.key);
});

Events

Event Description
knack-page-render.any Triggered by all pages
knack-page-render.page_1 Triggered by a single page specified by key

Parameters

Parameter Description
page a JSON object of the page and views it contains

View Render

// Change "view_1" to the view you want to listen for
$(document).on('knack-view-render.view_1', function(event, view, data) {
  // Do something after the view renders
  alert('listener for view: ' + view.key);
});

Events

Event Description
knack-view-render.any Triggered by all views
knack-view-render.view_1 Triggered by a single view specified by key
knack-view-render.type Triggered by all views of the specified type. Replace “type” with form, menu, search, list, calendar, search, or details

Parameters

Parameter Description
view The view (JSON object)
data Array of the records currently visible (JSON objects)

Record Events

Records Render

// change "view_1" to the view you want to listen for
$(document).on('knack-records-render.view_1', function(event, view, records) {
  // Do something after the records render
  alert('listener for records, # of records is: ' + records.length);
});

Events

Event Description
knack-records-render.any Triggered by all views
knack-records-render.view_1 Triggered by a single view specified by key

Parameters

Parameter Description
view The view (JSON object)
records Array of the records currently visible (JSON objects)

Record Create

// Change view_1 to the view you want to listen to
$(document).on('knack-record-create.view_1', function(event, view, record) {
  // Do something after the record is created via form submission
  alert('created a new record for view: ' + view.key);
});

Events

Event Description
knack-record-create.any Triggered by all forms
knack-record-create.view_1 Triggered by form specified by key

Parameters

Parameter Description
view The form (JSON object)
record The created record, including its ID (JSON object)

Form Submit

// Change view_1 to the form view you want to listen to
$(document).on('knack-form-submit.view_1', function(event, view, record) {
  alert('Form submitted!');
});

Events

Event Description
knack-form-submit.any Triggered by all forms
knack-form-submit.view_1 Triggered by form specified by key

Parameters

Parameter Description
view The form (JSON object)
record The created record, including its ID (JSON object)

Checkout Submit (Payment Complete)

// Change page_1 to the page with your checkout view
$(document).on('knack-checkout-submit.page_1', function(event, view, record) {
  // Do something after the payment has gone through
  alert('Payment successful');
});
Event Description
knack-checkout-submit.page_1 Triggered by checkout view on page specified by key

Parameters

Parameter Description
view The form (JSON object)
record The created record, including its ID (JSON object)

Record Update (Form)

// Change view_1 to the form view you want to listen to
$(document).on('knack-form-submit.view_1', function(event, view, record) {
  alert('Form submitted!');
});

Events

Event Description
knack-record-update.any Triggered by all forms
knack-record-update.view_1 Triggered by form specified by key

Parameters

Parameter Description
view The view (JSON object)
record The updated record (JSON object)

Record Update (Inline Table Edit)

// Change view_1 to the table view you want to listen to
$(document).on('knack-cell-update.view_1', function(event, view, record) {
  // Do something after the inline edit
  alert('updated a record for table view: ' + view.key);
});

Events

Event Description
knack-cell-update.view_1 Triggered by inline edits in table specified by key

Parameters

Parameter Description
view The view (JSON object)
record The updated record (JSON object)

Record Delete

// Change view_1 to the view you want to listen to
$(document).on('knack-record-delete.view_1', function(event, view, record) {
  // Do something after the record is deleted
  alert('Deleted record with ID: ' + record.id);
});

Events

Event Description
knack-record-delete.any triggered by all views
knack-record-delete.table triggered by all tables [table
knack-record-delete.view_1 triggered by form specified by key

Parameters

Parameter Description
view a JSON object of the view.
record a JSON object of the record deleted that was stored in the view.

JavaScript Utility Functions

The following functions are available within the client side of your own Knack apps; they can only be used from within your app’s custom JavaScript.

These are simple function calls you can use in order to get information about your app or user, trigger specific interface events, and more.

Retrieving User Properties

The following JavaScript utility functions are provided for accessing the logged-in user’s properties and user roles. These functions will only relevant provide information after a user has logged-in.

Knack.getUserAttributes()

This function gets you all of the logged-in user's information stored in his/her record. See the table below for how the basic information is returned.

If a user is not logged-in, the string “No User Found” will be returned.

Attribute Description
email a string of the logged-in user’s email
name a string of the logged-in user’s name
id a string of the logged-in user’s record id
roles an array of object-keys that the user is assigned to
example [‘object_1’, 'object_2’]
values the complete values from the user record, indexed by field
example {'field_1’: 'test@knack.com’, 'field_2’: 'Tester’}

Knack.getUserToken()

Returns the logged-in user's token (string).

Knack.getUserToken();
//"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTU0N2VhNjVkMmE3MzgxNDA5MjU3YWQzIiwiYXBwbGljYXRpb25faWQiOiI1MzllNjYzYjZlNTNlY2M1NjAwMDBiNzkiLCJpYXQiOjE0MzA3NzY0NDF9.g7C3iJexG3Na2dt6m_vumheXLUe1H_4NEYKziH9Gtm8"

Knack.getUserRoles()

When no parameter is provided, this function returns an array of object keys representing the user roles to which the user is assigned.

Knack.getUserRoles(); // ['object_1', 'object_2']

If you pass in an object key - as a string - this function will return true if the user has access to that role, or false if not:

Knack.getUserRoles('object_1'); // true

Interface Events

Coming soon!

Coming soon!

Load External JavaScript Files

// The first parameter is an array of files to load.
// The second parameter is a function to execute after all the libraries have completed loading.
LazyLoad.js(['https://cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js'], function () {
    alert('All my files have completed loading!');
});

// load some external CSS files
var css_files = ['https://cdnjs.cloudflare.com/ajax/libs/1140/2.0/1140.css', 'https://cdnjs.cloudflare.com/ajax/libs/SlickNav/1.0.3/slicknav.min.css'];
LazyLoad.css(css_files, function () {
    alert('All my CSS files have completed loading!');
});

Loading JS Files with Event Handlers from Outside of the Builder

If you lazyload JavaScript with event handlers, you will need to wrap any event handlers within the KnackInitAysnc() function. This is a global function which Knack calls when it has completely loaded.

It also passes in the Knack jQuery object as the first parameter, $. Knack uses a sandboxed jQuery object in order to prevent conflicts with other jQuery versions you may be using.

Let's take a look at an example where our external JavaScript file includes an event handler:

// file.js
$(document).on('knack-scene-render.any', function(event, scene) {
  alert('Page rendered!');
});

Now let's load file.js into our Knack app, wrapping our LazyLoad call in KnackInitAsync():

// In the Builder
KnackInitAsync = function ($, callback) {
  // REQUIRED: Explicitly include jQuery
  window.$ = $;
  // Load the file from wherever it is hosted
  LazyLoad.js(['https://example.com/javascript-files/file.js'], function () {
    console.log('Loaded external files!');
    // REQUIRED: Tell the Knack app to go ahead and finish loading
    callback();
  });
};

Notes on KnackInitAsync()

JavaScript & jQuery Examples

The following code examples have come from common requests, projects we've loved, code which users have shared with us, and other sources.

Many of these examples showcase how easily jQuery can be leveraged to make considerable changes UX within your app, such as hiding or showing specific elements in a specific view when certain conditions are met.

Another very common use for jQuery in custom code is to make AJAX calls from your Knack app. These often go to external - or you own app's - APIs. For some examples of this, take a look at our API code examples.

Replace text all submit buttons

// This would update the text on every single view across the app
$(document).on('knack-scene-render.any', function(event, scene) {
  $(".kn-submit input[type=submit]").attr("value", "Press me");
});

Add color to a specific field value in a view

This allows you to add color to specific fields. For example, showing the word “Expired” or “Rejected” in red text, or maybe making the text green if it is “Approved.” This example will change the text to red if the value is “Expired” otherwise it sets it to green.

// Replace view_1 and field_2 with your view and field keys
$(document).on('knack-view-render.view_1', function(event, view, data) {
  $("#view_1 td.field_2").each(function() {
    var textColor = ($(this).find("span").text().trim() == "Expired") ? "#ff0000" : "#1c631f"; // #ff0000 is red and the #1c631f is green
    $(this).css("color", textColor);
    })
});

This example allows you to move the Signup link above the log in form. You could also add other CSS to the link to make it more visible if you wanted.

// Replace view_1 with your view key
$(document).on('knack-view-render.view_1', function(event, scene) {
  $("#view_1 .register").insertAfter($("#view_1 .view-header"));
});

Custom form validation

This example allows you to set a specific validation requirement for a field when submitting a form.

// Replace view_1 and field_2 with your view and field keys
$(document).on('knack-view-render.view_1', function(event, view, data) {
  $("#view_1 .kn-submit input[type=submit]").on("click", function() {
    // If this value in the form doesn't equal "SpecificValue" then prevent the form from submitting
    if ($("#view_1-field_2").val() != "SpecificValue") {
     alert ("These are not the droids you are looking for.");
     return false;
    }
  })
});

Add checkboxes to a table

This example allows you to add checkboxes to a table, including a check all/none checkbox in the header.

// Function that adds checkboxes
var addCheckboxes = function(view) {
  // Add the checkbox to to the header to select/unselect all
  $('#' + view.key + '.kn-table thead tr').prepend('<th><input type="checkbox"></th>');
  $('#' + view.key + '.kn-table thead input').change(function() {
    $('.' + view.key + '.kn-table tbody tr input').each(function() {
      $(this).attr('checked', $('#' + view.key + '.kn-table thead input').attr('checked') != undefined);
    });
  });
  // Add a checkbox to each row in the table body
  $('#' + view.key + '.kn-table tbody tr').each(function() {
    $(this).prepend('<td><input type="checkbox"></td>');
  });
}
// Add checkboxes to a specific table view (view_1). Replace view_1 with your view key
$(document).on('knack-view-render.view_1', function (event, view) {
  addCheckboxes(view);
});
// Cycle through selected checkboxes. Use this in any code that needs to get the checked IDs
$('#view_5 tbody input[type=checkbox]:checked').each(function() {
  // add code here to get record id or row value
  var id = $(this).closest('tr').attr('id'); // record id
});

This will move the backlink (ex. “Back to Client”) at the bottom of the page, directly below the crumbtrail (ex. Client > Client Details) at the top of the page. This will change will affect every page in the app.

$(document).on('knack-scene-render.any', function(event, page) {
   $(".kn-view.kn-back-link").insertAfter( $(".kn-view.kn-info.clearfix") );
});

Disable form input field

This example will disable the text input field in a form.

// Replace scene_1, view_1, and field_1 with your page, view, and field keys
$(document).on('knack-page-render.scene_1', function(event, page) {
    $('#view_1 #field_1').attr('disabled', 'disabled');
});

This example will replace the HTML for all back links in the app.

// Replace "Hello world!" with the text you would like to add to your app
$(document).on('knack-page-render.any', function(event, page) {
  $(".kn-back-link a").html("Hello world!")
});

Convert input text to uppercase

This example will convert any text you input in the specified text to UPPERCASE as you type it.

$(document).on('knack-view-render.any', function (event, view, data) {
  // Remove #field_1 so it works for ALL inputs
  // Use $('input:not(#email, #password)') to exclude emails and passwords for logins
  // Replace field_1 with your field key
  $('input#field_1').keyup(function() {
      this.value = this.value.toUpperCase();
  });
});

Capitalize input text

This example will capitalize any text in the specified input and ignore all characters after the first one.

$(document).on('knack-view-render.any', function (event, view, data) {
// Remove #field_1 so it works for ALL inputs
  $('input#field_1').keyup(function() {
      this.value = this.value.charAt(0).toUpperCase() + this.value.slice(1);
  });
});

Change the options in the time field

This example will change the range of time options available in the timer field in a form.

/* Change the field and view numbers to match the view and field in your app.*/
$(document).on('knack-view-render.view_8', function(event, view, data) {
  $('#view_8-field_59-time').timepicker({
    'minTime': '2:00pm',
    'maxTime': '11:30pm',
    'showDuration': true
  });
});

Change the 'add filter’ text

This example will change the 'add filter’ text for any view.

$(document).on('knack-view-render.any', function(event, view, record) {
  $('.kn-add-filter').text('Advanced Search')
});

Redirect the user after an e-commerce checkout

This example will redirect the user after they submit a Checkout form.

$(document).on('knack-checkout-submit.any', function(event, view, record) {
  window.location.replace('https://yoururlgoeshere.com');
});

CSS

Knack apps are easy to customize with your own CSS (Cascading Style Sheets).  If you are familiar with CSS or have access to web designers you can completely customize the look and feel of your Knack app.

How to Add Your CSS

Publishing and adding to your site

When you publish your Knack app to your own website or blog, that app is added directly to your website’s HTML code. This means that it will follow any CSS rules you’re including for that web page.  A Knack app will typically follow your font and link color styles by default.

You can also add additional CSS styles to modify the default Knack styles. Any styles added to the same page you publish your app to will be applied to the HTML of your Knack app.

Adding directly to your app

You can also add CSS styles directly to your app. Click on the name of your app in the upper-left corner, Settings, then API & Code. You’ll have a tab for adding your own styles:

Code in settings menu

Code CSS

Identifying Elements to Change

Types of CSS Selectors

In order to target specific aspects of the page, you will need to use CSS selectors. These can either be Class, ID, or Tag selectors:

Using these will allow you to narrow down which areas of your app you would like to target with your CSS.

Finding the Right CSS Selectors

The easiest way to get started is to use a DOM inspector that comes with modern browsers like Chrome and Firefox.  You can inspect an element in the Knack app and the inspector will show you which classes and IDs you should modify.  The following is an example of inspecting an element in Chrome, where you can see that “My Service REquests” can be targeted using the “h2” tag:

Prioritize your styles

// Specificity
.kn-content #kn-app-menu ul {
  background: #DDD;
}

// !important
#kn-app-menu ul {
  background: #DDD !important;
}

In order to modify the Knack styles you’ll need to assign a priority to your own styles that become more important than the default Knack styles.  Styles will cascade (the first “c” in CSS) by priority, so you need to make sure your styles take higher priority than the Knack styles.

There are two ways to do this.

Learning more about CSS

You can find more details about CSS in general and on the information in this section here:

CSS Examples

Below are fairly simple snippets which address several of the most common scenarios which require custom CSS.

Set the width of a table column

This example forces a set width for a column.

/* Change the view_1 to the key of your table view */
/* Change the field_1 to the key of the field of the column you want to set */
#view_1 th.field_1 {
  width: 100px; /* width in pixels */
}

This example hides the Account Settings link found at the top of all scenes:

/* Change the kn-scene_1 to the key of your current scene */
/* To apply this to all Account Settings links in your app, remove the #kn-scene_1 */
#kn-scene_1 a.kn-account_settings {
  display: none;
}

Hide the log-in form (primarily for SSO)

You might find this example useful if you only want your users to sign-in through SSO.

/* Change view_1 to the key of your current view */
/* To apply this to ALL logins, remove #view_1 */
#view_1 div.kn-login-form {
  display: none;
}

Only show group totals in a table (hide grand total)

This example will hide the last Totals row in a grouped table. Only displaying totals per group.

/* Change view_1 to the key of your current view */
#view_1 tr.kn-table-totals:nth-last-child(1) {
  display:none !important;
}

Change a page’s font

This example will change the Font styles for a specific page. Choose from a variety of fonts: http://www.cssfontstack.com/

/* Change scene_2 to the key of the page whose page you want to change */
#kn-scene_2 {
  font-family: "Times New Roman", Georgia, Serif;
}

Change a label’s background color

This example will change the background color for all labels in a view.

/* Change the view_1 to the key of your table view */
/* Set your own color by changing the #feeeee value */
#view_1 .kn-label  {
  background-color:#feeeee;
}

Hide the crumbtrail at the top of the page

This example will hide the crumbtrail (Ex. “Project > Project Details”) at the top of the page for the entire app.

.kn-scene .kn-crumbtrail {
  visibility: hidden;
}

This example will hide the entire logo header at the top of the app.

#knack-logo {
  display: none;
}

Hide the menu

This example will hide the menu from the top of the app. This can work well in combination with the Hide the Logo example above if you just want to include a single page or form without the rest of your app.

#kn-app-menu {
  display:none !important;
}

Hide a cell from pivot table summary row

This example will remove the third cell in the final (summary) row of a pivot table, allowing you to hide, for example, an unnecessary sum of a column’s data.

#view_1 tr.kn-table_summary td:nth-child(3) {
  font-size: 0;
}

Remove the logo gradient

This example will remove gradient in the Logo banner so you can use a flat color. You can specify your own background color with this as well.

#knack-logo {
  background: none !important;
  background-color:#023F74 !important;
}

Display table headers in every printed page

When you print a table with hundreds of rows, your table headers only displays in the first page. This example will display the Table headers in every page.

/* Use this for ALL tables */
@media print {
    .kn-table-table thead {
        display: table-header-group;
    }
}
/* Use this for a SPECIFIC table. Replace #view_1 with your specific view. */
@media print {
    #view_1 .kn-table-table thead {
        display: table-header-group;
    }
}

Change the font when printing a page

This example will change the font style when you print a page. You can find a list of web-safe fonts here.

/* Use this for ALL tables */
@media print {
    .kn-table-table thead {
        display: table-header-group;
    }
}
/* Use this for a SPECIFIC table. Replace #view_1 with your specific view. */
@media print {
    #view_1 .kn-table-table thead {
        display: table-header-group;
    }
}

Resources

The Knack community offers multiple resources to help build your app, discuss ideas, and share code libraries.

The Builder Network

Looking to get - or give - a hand? The Knack Builder Network is a network of vetted Knack builders that are available to help build your projects.

If you'd like some hands-on assistance with JavaScript, CSS, other API integrations, or even just help with designing or building your app, the Builder Network is a great resource. You can write up the specifications for what you need done - along with your budget - and reach out for offers from Knack-vetted developers and app builders.

On the other hand, if you would like to apply to become a Builder yourself, you can do so here as well.

See our page on the Builder Network for more information.

Knack Forums

Knack has an outstandingly active community. If you're stuck and looking for a hand from a fellow Knack user - or looking to share your own knowledge - consider perusing or posting in our forums. Both the API & Customization and How Do I...? forums are great places to get and give help.

If you've built something you'd like to show off and explain to other developers or builders, we'd love to see it in the Show and Tell forum!

Third-Party Wrappers

The following libraries have been created by Knack users to help connect different coding languages with the Knack API. Please get in touch if you've created a code resource that could help other Knack developers.

NodeJS

knackhq-client

Python

knackhq

Ruby

knackhq-client