For this documentation we are going to use
curl
with the headers:
accept
: application/json
content-type
: application/json
If you prefer you can create an alias in your current shell, add to your shell script profile, or set the equivalent in the software you are using, the alias is as follow:
alias curl='curl -s -H "accept: application/json" -H "content-type: application/json"'
Another standard of this documentation is that whenever
we share the output it is going to be formatted with the
jq
command line software.
A simple example of jq
is:
$ echo '{"ok":true}' | jq { "ok": true }
The final standard is: with the exception of the commands
in this introduction section all of the other commands
are not going to have any shell indicator like
$
, #
, %
, or any other.
There are several ways to authenticate on the API, including a re-authentication one (or refresh token if you will).
We recommend basic auth only in development mode or when writing simple scripts (the kind that does not do more than 5 requests). Because this is a slow form of authentication.
For example to get information about your session:
curl \ -X GET \ -u [email protected]:password \ "https://api.simpleserviceorder.com/_session"
The output is:
{ "ok": true, "userCtx": { "name": "usr-9fcc027ca99e44468227bef4c4440d5c", "roles": [ "acc-1e36d77db5434f3cbded1bb07db403c1" ] } }
Getting a token means you authenticate with a slow process, and after that you only use the token.
Using username and password is slow because we have algorithms in place to prevent Timing attacks
The endpoint to get a token is POST /_session
.
Here you can use Basic Auth for the request:
curl \ -X POST \ -u [email protected]:password \ "https://api.simpleserviceorder.com/_session"
Or add the information within the JSON payload:
curl \ -X POST \ "https://api.simpleserviceorder.com/_session" \ -d '{"username":"[email protected]","password":"password"}'
In both cases the JSON output is:
{ "ok": true, "name": "usr-9fcc027ca99e44468227bef4c4440d5c", "roles": [ "acc-1e36d77db5434f3cbded1bb07db403c1" ] }
The JWT token is in the header of the HTTP request, if
you add the -i
to the curl
call
it is going to output something like this:
HTTP/2 200 set-cookie: AuthSession=eyJhbGciOi; path=/; expires=Wed, 08 Feb 2023 17:59:47 GMT; HttpOnly; SameSite=Strict { "ok": true, "name": "usr-9fcc027ca99e44468227bef4c4440d5c", "roles": [ "acc-1e36d77db5434f3cbded1bb07db403c1" ] }
eyJhbGciOi.eyJ1c2VyX2lkIjoiOWZjYzAyN2MtYTk5ZS00NDQ2LTgyMjctYmVmNGM0NDQwZDVjIn0.7GzetforEg8U1-PrsjWNELa2iMaEsD1JJNoY82vh52s
to eyJhbGciOi
With that token you can use to authenticate the following requests.
For example, to get your session with that token:
curl \ -X GET \ -H "Authorization: Bearer eyJhbGciOi" \ "https://api.simpleserviceorder.com/_session"
And the output is the same as before:
{ "ok": true, "name": "usr-9fcc027ca99e44468227bef4c4440d5c", "roles": [ "acc-1e36d77db5434f3cbded1bb07db403c1" ] }
This is a particular case in which you have access to a
token and wants to get a new one before it expires using
the same POST /_session
request:
The recommended way to re-authenticate is to call the endpoint
POST /_session
with the autorization you
already have, be it Basic Auth, a Bearer token in the
header, or a request using the HttpOnly cookie.
Bearer example:
curl \ -X POST \ -H "Authorization: Bearer eyJhbGciOi" \ "https://api.simpleserviceorder.com/_session"
Basic Auth example:
curl \ -X POST \ -u [email protected]:password \ "https://api.simpleserviceorder.com/_session"
As a final note, if you know the user id you can replace it instead of using an email address:
curl \ -X GET \ -u usr-9fcc027ca99e44468227bef4c4440d5c:password \ "https://api.simpleserviceorder.com/_session"
In the previous session you saw the Session
output as JSON together with the token. Here is a short
explanation of its elements.
The Session
JSON:
{ "ok": true, "name": "usr-9fcc027ca99e44468227bef4c4440d5c", "roles": [ "acc-1e36d77db5434f3cbded1bb07db403c1" ] }
Its attributes are:
ok
name
ID
of the user
roles
roles
exposes an array with
ID
of databases the user has some kind
of permission, be it read and/or write.
Database
is what the frontend displays as
Group profile. For the purpose of
this documentation we are going to call
database.
A database is basically an aggregation of data that several users have access to. Some other software may call it Group, Organization, or Team.
database and User
are the only
two things that do not depend on anything to exists.
As you could see in the session output, the
roles
attribute exposes an array with the
unique name of each database the user has
permission.
Each user can create up to 5 free groups, we recommend using this feature to test any API integration.
Besides that we also have one kind of record that let you perform tests in the API even in a production environment.
Crystal
records, e.g., this
is an API-only feature.
This kind of record lets you manage Crystal
,
each has the following fields:
_id
: Unique ID of the record_rev
: The locking code lets you perform updates, auto generatedname
: The name of the
Crystal
, because this is a testing
endpoing, there is no right or wrong information to
store here, but it cannot be an empty string, Text
age
: The age of the
Crystal
, the number stored in this field
has to be lower than 125, Integer
name
field of
Crystal. To force an update you can delete
the Crystal and create a new one with the
same _id
.
POST /:database
When you sign up the name of your database and the
_id
of your User are random, we do
that to prevent databases and Users to
reuse _id
s as this can lead to security
vulnerabilities.
But when you manage the records within your organization
you must provide the _id
yourself.
As you could see from the previous examples the
_id
field is a
Universally unique identifier
plus a 3 letter prefix for that particular kind of record.
If you don't want to use a random _id
for
your records but an incremental one, you can replace all
characters with 0
except for the
prefix and the -
(dash) and place
the number to the end, example:
crs-00000000000000000000000000000157
We do very little validation in the _id
field, so far the only requirement is for it to be
hexadecimal.
Creating a record with valid data:
curl \ -X POST \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1" \ -d '{"_id":"crs-041fa18a2f77410d9bf4e07fa33abeeb","name":"John Doe","age":120}'
Returns:
{ "id": "crs-a9d89d7963a2ee2f1b92f73c2dfb73a0" }
Creating a record with missing mandatory field:
curl \ -X POST \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1" \ -d '{"name":"John Doe", "age": 120}'
Returns:
{ "error": "unprocessable_entity", "reason": { "_id": "can't be blank" } }
Creating a record with invalid data:
curl \ -X POST \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1" \ -d '{"_id": "crs-ee4371b193ac4b4191cb27624c7442c2", "name":"John Doe", "age": 210}'
Returns:
{ "error": "unprocessable_entity", "reason": { "age": "less-than-or-equal-to" } }
GET /:database/:document_id
With the ID of the record to query you can call the API with:
curl \ -X GET \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1/crs-ee4371b193ac4b4191cb27624c7442c2"
Returns:
{ "_id": "crs-a9d89d7963a2ee2f1b92f73c2dfb73a0" }
If you don't tell which fields you want in your request
we are going to reply only with _id
and
_rev
.
Example with the fields
query parameters:
curl \ -X GET \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1/crs-ee4371b193ac4b4191cb27624c7442c2?fields=_id,_rev,name,age,created_at,updated_at"
Returns:
{ "_id": "crs-ee4371b193ac4b4191cb27624c7442c2", "_rev": "1-25907e94b8cec1aa102595d415fbb36b", "name": "John Doe", "age": 120, "created_by_id": "usr-9fcc027ca99e44468227bef4c4440d5c", "updated_by_id": "usr-9fcc027ca99e44468227bef4c4440d5c", "created_at": "2023-05-13T23:16:24.792Z", "updated_at": "2023-05-13T23:16:24.792Z" }
PUT /:database/:document_id
Some rules are mandatory to this endpoint,
/accounts
endpoint is required here:
rev
in the query parametersAn example of updating a Crystal:
curl \ -X PUT \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1/crs-ee4371b193ac4b4191cb27624c7442c2?rev=1-25907e94b8cec1aa102595d415fbb36b" \ -d '{"age":20}'
And the response is the same as the POST
:
{ "id": "crs-ee4371b193ac4b4191cb27624c7442c2", "ok": true }
DELETE /:database/:document_id
You also need the rev
query parameters to delete a record.
An example request to delete a record is:
curl \ -X DELETE \ "https://api.simpleserviceorder.com/acc-1e36d77db5434f3cbded1bb07db403c1/crs-ee4371b193ac4b4191cb27624c7442c2?rev=2-f32d250e7d4ce0a07c99dd2d14095582"
And the response is the same as create or update:
{ "id": "crs-ee4371b193ac4b4191cb27624c7442c2", "ok": true }
If you forget or pass on a wrong rev
you get
the same response as before:
{ "error": "conflict", "reason": "Document update conflict." }
POST /:database/_find