Fetching Resources
Web1on1 accepts many options to manipulate list and detail results for top-level resource types. These options can be added to your request via the querystring to filter, limit, sort and transform your resultsets.
Examples:
GET <apibase>/conversations
GET <apibase>/conversations?field=value
GET <apibase>/conversations?populate=contact,messages
GET <apibase>/conversations?sort=field,-field2
GET <apibase>/conversations?offset=10&limit=10
<apibase>: https://api.web1on1.chat/v2
Mandatory Query Parameters
Most resource listings can have zero or more query filters.
For fetching messages at least one of the following parameters should be supplied: id, organization, conversation, type, role or createdAt.
For fetching conversations using a query for a meta variable, at least one of the following parameters is required: id, type, participants.user, status, createdAt, categoryIndex, category, organization, createdAt or updatedAt.
Example:
GET <apibase>/conversations?meta.intent=greeting&type=contact
In this example, when searching for a meta.intent, we limit the results to only conversations with the "contact" conversation type.
Reserved Query Parameters
In general, document field names can be used as query parameters to filter search result. Additionally, a number of specialized parameters can be used to limit, sort and transform the results. These special parameters are:
- sort - Sorts by the given fields in the given order, comma delimited. A - sign will sort descending.
- limit - Limits the number of returned results.
- offset - Skips a number of results. Useful for pagination when combined with limit.
- select - Comma-delimited list of fields to select.
- populate - Comma-delimited list of fields to populate.
Sorting
Getting a sorted list is as easy as adding a sort querystring parameter with the property you want to sort on. /users/?sort=name will give you a list sorted on the name property, with an ascending sort order. /users/?sort=-name will return the same list as before with a descending sort order.
GET <apibase>/users?sort=name
GET <apibase>/users?sort=-name
Response documents may be sorted by multiple criteria. Here's how you'd sort the collection by name in ascending order, then by email in descending order.
GET <apibase>/users?sort=name,-age
Pagination
By default, endpoints paginate results at 200 records per page, ordered by the _id field. Pagination is supported via offset and limit query parameters. When implementing pagination you might want to use offset and limit parameters, to skip a given amount of items or limit to a set amount of items.
GET <apibase>/users?offset=10&limit=10
offset
Skip sending the first n matched documents in the response. Useful for paging.
GET <apibase>/users?offset=5
limit
Limit the response document count to n at maximum. The default limit is 200.
GET <apibase>/users?limit=5
Examples
- GET
/users/?limit=5 will give you the first 5 items - GET
/users/?offset=5 will skip the first 5 and give you the rest - GET
/users/?offset=5&limit=5 will skip the first 5 and then give you the second 5
Response Headers
Resultsets are accompanied by meta information in the response headers, which can be useful for pagination. The relevant response headers are:
- X-Total: total number of results;
- X-Total-Pages: total number of pages;
- X-Per-Page: the number of results per page, corresponding to the limit parameter;
- X-Page: the page number in a paginated set of results;
- X-Range: the offset and the index of the last paged result, separated by a hyphen.
For example, consider a request for /users/?offset=5&limit=5, where the total number of users available is 9. The corresponding response headers in this case would be:
X-Total: 9
X-Total-Pages: 2
X-Page: 2
X-Per-Page: 5
X-Range: 6-9
A list of these response headers are specified in the standard Access-Control-Expose-Headers header, making them accessible to browser-based applications.
Link Header
Page navigation links are provided by the API to help your application request further pages of results. API responses, for paged results, contain HTTP Link headers providing a URL used to request other results pages.
Link: <https://api.web1on1.chat.v2/users>; rel="first",
<https://api.web1on1.chat<apibase>/users>; rel="prev"
Four relationship types are possible:
- first shows the URL of the first page of results. Not present if the response is the first page in a collection.
- prev shows the URL of the immediate previous page of results. Not present if the response is the first page in a collection.
- next shows the URL of the immediate next page of results. Not present if the response is the last page in a collection.
- last shows the URL of the last page of results. Not present if the response is the last page in a collection.
Pagination in the SDK
Pagination is available if the api is used Promise based. If you use callback methods, you will have to arrange Pagination yourself by reading the headers). Promise based is the future anyway.
Pagination can be used in two different ways in the SDK. Getting the resources by itteration or all at once.
Pagination in the SDK by iteration
Iteration is the cleanest way and works like this:
const Sdk = require('../lib/chipchat');
const sdk = new Sdk({
token: process.env.TOKEN
});
const conversation = '<your test conversation id with at least 20 messages>';
(async () => {
// activate pagination with all defaults:
const iterableMessages = sdk.messages.list({ conversation, pagination: { iterate: true, limit: 100 } });
for await (const message of iterableMessages) {
console.log('message: ', messages.id, message.text);
}
})();
When storing this code in a file called thecode.js and running this code with DEBUG=*rest* node thecode.js
you will see after the first 100 messages are shown there is a slight delay in the loop as it is getting the next 100 messages.
iterate will make the pagination return an iteratable object
limit will make the slice size be 100 messages per request untill all messages are retreived or the default max countLimit of 2000 is reached.
By setting countLimit: Infinity will force getting all messages unless the default requestLimit of 10000 requests is reached
By also setting requestLimit: Infinity will force getting all messages. period.
Pagination in the SDK by using .then
But you can also get them without itterating like this:
const Sdk = require('../lib/chipchat');
const sdk = new Sdk({
token: process.env.TOKEN
});
const conversation = '<your test conversation id with at least 20 messages>';
sdk.messages.list({ conversation, pagination: { limit: 100 } }).then(messages => {
for (const message of iterableMessages) {
console.log('message: ', messages.id, message.text);
}
));
The difference is subtle but important. This style looks a bit cleaner but will retreive and store all messages in an array before returning them. This can cost a lot of memory when your request is big.
Pagination parameters in the SDK
You can use the following settings when using pagination:
name | default | description |
---|---|---|
iterate | false | Will make the pagination return an paginated itterable object |
limit | 100 | How many result are retrieved per slice |
backoff | 100 | How much time the pagination will wait in between page calls (in ms) |
countLimit | 2000 | How many results will be returned in total. Set to Infinity to have no limit. use wisely |
requestLimit | 10000 | Max nr requests. Set to Infinity to have no limit. use wisely |
You can also move the pagination configuration to the Sdk initilization call, activating pagintion for all GET calls.
Field Selection
Selecting the entity-properties you need (collections and details)
If you only need a few properties instead of the entire model, you can ask the service to only give just the properties you need. The select query parameter sets which fields should be selected for response documents.
A GET request to /users?select=name,email would result in something like:
[
{
"id": "543adb9c7a0f149e3ac29438",
"name": "user1",
"email": "user1@test.com"
},
{
"id": "543adb9c7a0f149e3ac2943b",
"name": "user2",
"email": "user2@test.com"
}
]
As can be seen, the id field is always returned irrespective of selected fields.
Excluding properties from the selection
Instead of specifying which properties to select, you can exclude properties from payloads by prepending the select parameter with a minus character.
A GET request to /conversations?type=contact&select=-messages,-participants would result in a conversations listing with messages and participants excluded from the payload.
CAVEAT
It's not possible to mix inclusion and exclusion of properties; for example the following would be invalid: ?select=-participants,messages.
Field Population
Populating referenced sub-entities
When resulting documents contain properties referencing other entities, you can ask the API service to populate them for you. The populate parameter sets which fields should be populated for response documents.
A GET request to /conversations will result in:
[
{
"id": "542edff9fffc55dd29d99346",
"contact": "591506e18a280627f8dcfede"
...
}
]
A GET request to /conversations?populate=contact will expand the contact for all conversations found, resulting in:
[
{
"id": "542edff9fffc55dd29d99346",
"contact": {
"organization": "591508af8a280627f8dcfef1",
"conversation": "222fff111333000000000fff",
"profile": {
"name": "John Doe",
"gender": "male"
},
...
},
...
}
]
Multiple populate parameters may be specified, separated by a comma. Populated fields will be fetched alongside with, but independent of selected fields (using the select parameter). Population can be applied both to resource listings and individual resources.
Populating arrays of resource IDs
Population also works for resources that have an array of resource IDs as a property. As an example, a conversation has a list of message IDs; to populate all message objects, use:
GET <apibase>/conversations?populate=messages
[
{
"id": "542edff9fffc55dd29d99346",
"messages": [
{
"type": "chat",
... rest of full message
}
]
...
}
]
Populating Nested Properties
To populate nested properties (inside arrays or objects), use the dot-notation to specify the path to populate. For example, to expand all user IDs into user objects in a conversation's list of participants, use:
GET <apibase>/conversations?populate=participants.user
[
{
"id": "542edff9fffc55dd29d99346",
"participants": [
{
"user": {
"id": "5a4fd7419feede6a19b18713"
"name": "John Johnson",
... rest of full user
},
... rest of participant
}
]
...
}
]
Selecting wich fields of the related document to populate
When populating referenced resources, you may be only interested in some properties of the underlying resource - if only to keep the payload size managable. The select
parameter mentioned earlier only applies to the immediate resource; to select a set of fields for populated resources, append the populated property with a colon (:) and one or more fields separated by a pipe (|) symbol.
For example, say we want to fetch a single conversation with just the conversation status, the name of the owning organization, the name of participants and two message properties (type and text). The request would look like this:
>GET <apibase>/conversations/5a4fd7419feede6a19b18712?select=status& //
populate=messages:type|text,organization:name,participants.user:givenName|familyName
This would result in a structure similar to:
{
"id": "5a4fd7419feede6a19b18712",
"status": "closed",
"organization": {
"name": "Acme, Inc",
"id": "5a53e8e4dcfa875e82ed2ab3"
},
"participants": [
{
"user": {
"givenName": "Edgar",
"familyName": "Poe",
"name": "Edgar Poe",
"id": "5a53e98944cf9c622117fd28"
}
}
],
"messages": [
{
"type": "postback",
"text": "startcbot",
"id": "5a4fd7419feede6a19b18714"
},
{
"type": "command",
"text": "/join",
"id": "5a4fd7419feede6a19b18713"
}
]
}
CAVEAT
As you can see, resource IDs are always provided, regardless of field selection. In addition to ids, "derived" resource properties (like participants.user.name) are always included; however these derived property values may be inaccurate if based on non-populated fields. In the above example, any participant's additionalName is not part of the generated "name", because additionalName (here: "Allan") was not part of the populated fieldset.
Search Filters
Any query string parameters, other than those mentioned, will be matched against document properties as search filters to narrow down the resultset. You can ask the service for equality, or values greater or less than, give it an array of values it should match to, or even a regular expression match.
Please note that repeating the same field multiple times in the query string is not supported.
GET <apibase>/messages?text=!
GET <apibase>/messages?text=~regex
GET <apibase>/messages?text=value
GET <apibase>/messages?text=>value
GET <apibase>/messages?text=>=value
GET <apibase>/messages?text=<value
GET <apibase>/messages?text=<=value
GET <apibase>/messages?text=>value1,<=value2
GET <apibase>/messages?text=!=value
GET <apibase>/messages?text=[value1,value2]
GET <apibase>/messages?text=![value1,value2]
Filter Operators
While exact matching using just the equals sign is most common results filter, various types of search filters may be specified by prefixing or surrounding query value(s) with special operators:
Filter | Operator | Example | Description |
---|---|---|---|
equal | (none) | /users?gender=male |
return all male users |
not equal | != |
/users?gender=!=male |
returns all users who are not male |
exist | (none) | /users?gender= |
return all users with any gender specified |
not exist | ! |
/users?gender=! |
returns all users without a gender |
greater than | > |
/users?age=>18 |
returns all users older than 18 (age should be a number property) |
greater than or equal to | >= |
/users?age=>=18 |
returns all users 18 and older |
less than | < |
/users?age=<30 |
returns all users age 29 and younger |
less than or equal to | <= |
/users?age=<=30 |
returns all users age 30 and younger |
between | >,< |
/users?age=>18,<=30 |
returns all users older than 18, but younger than or equal to 30 |
in | [] |
/users?gender=[female,male] |
returns all female and male users |
nin | ![] |
/users?age=![18,30] |
returns all users with age other than 18 or 30 |
regex | ~ |
/users?name=~oost |
returns all users with a name containing 'oost' |
nested array | {} |
/conversations?participants={role=admin;accepted=true} |
returns all conversations that are accepted by an admin participant' |
Use OR-criteria for alternate/multiple conditions
To perform OR-queries on multiple fields, separate them with a comma. OR-queries do not support the above operator prefixes, but always perform a case-insensitive regular expression match, or an exact match if the field represents a resource ID. For example:
GET <apibase>/organizations?name,displayName=acme
This would return all organizations where either name
or displayName
contain the string "acme".
Use dot-notation to search in nested objects
To search in nested data structures, use dot
-notation in your filters. For example, to find all users with their language preference set to Dutch, find the appropriate value in the user's settings object:
GET <apibase>/users?settings.locale=nl
Nested arrays can be searched for a single condition by using the dot-notation, too. For example, to find all conversations where a specific user is a participant:
GET <apibase>/conversations?participants.user=5da507d56f7cab2c446f4a50
If you specify multiple nested array fields, the search request will return all records that match any of the conditions (OR-query). For example, to find all organizations with categories that match 'car' and/or contain some specific form:
GET <apibase>/organizations?categories.name=~car&categories.forms=590c76bdde1b762f2113e81c
See the curly brackets method below to match an element on multiple conditions.
Use indexes to count nested array members
To limit by nested arrays, combine the index with the exist/non-exists operator. For example, to fetch all conversations with at least two participants, do
GET <apibase>/conversations?participants.1
Likewise, to fetch all conversations with no more than one participant, do
GET <apibase>/conversations?participants.1=!
Use curly brackets to find nested array members matching multiple fields
In some cases, you may want to find records that match a nested array member on multiple criteria. Using the dot-notation with multiple fields (see above) will not necessarily match the same array member - to look those records, wrap your conditions as key-value pairs inside curly brackets.
For example, to get all conversations in a user's inbox, do:
GET <apibase>/conversations?participants={user=5db4e0fbbf878c1e9643138a;inbox=true}
As an alternative, keys and values may be separated by a colon, and key-value pairs by a comma. Whitespaces are ignored. The following query is identical the previous one:
GET <apibase>/conversations?participants={ user: 5db4e0fbbf878c1e9643138a, inbox: true }
Examples
Get all active conversations:
GET <apibase>/conversations?status=active
Get all queued conversations for an organization:
GET <apibase>/conversations?status=queued&organization=5db4e3bf4c14a433f2f8b644
Get all active conversations for any participant of an organization:
GET <apibase>/conversations?status=active&participants.organization=5db4e3bf4c14a433f2f8b644
Get all active participants for an organization's closed conversations (lingering bots):
GET <apibase>/conversations?status=closed&participants={organization=5db4e3bf4c14a433f2f8b644;status=active}
Get all conversations where a user is active (has joined or accepted):
GET <apibase>/conversations?participants={user=5db4e0fbbf878c1e9643138a;active=true}
Get all conversations that a user has joined:
GET <apibase>/conversations?participants={user=5db4e0fbbf878c1e9643138a;active=true;accepted=false}
Get all conversations that a user has accepted:
GET <apibase>/conversations?participants={user=5db4e0fbbf878c1e9643138a;accepted=true}
Get all conversations in a user's inbox:
GET <apibase>/conversations?participants={user=5db4e0fbbf878c1e9643138a;inbox=true}
Get all conversations that a user is following, and has an inbox notification:
GET <apibase>/conversations?participants={user:5db4e0fbbf878c1e9643138a,follow:true,inbox:true}
Get all conversations that are being followed by an organization's users:
GET <apibase>/conversations?participants={organization=5db4e3bf4c14a433f2f8b644;inbox=true}
Get all conversations accepted by bots:
GET <apibase>/conversations?status=active&participants={role=bot;accepted=true}
Get all queued conversations with active joined-only bots:
GET <apibase>/conversations?status=queued&participants={role=bot;active=true,accepted=false}
Get all queued in any participant's subscribed channels:
GET <apibase>/conversations?status=queued&channels=[5ebdd19745e6ba59efa6b094]
Get all online users for any channel:
GET v2/channels?members.online=true
Get all online channels for a user:
GET <apibase>/channels?members={user=5db4e0fbbf878c1e9643138a;online=true}