For this documentation we are going to use
curl
with the headers:
application/json
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 the token and wants to get a new one before it expires.
We also built this feature as a transition from the API v3 which had access to the token in the web browser and using JavaScript.
In future versions we are going to deprecate this in
favor of calling POST /_session
or
GET /_session
to get a new token in the header.
curl \ -X POST \ "https://api.simpleserviceorder.com/_session" \ -d '{"token":"eyJhbGciOi"}'
And the output is the same as the other
POST /_session
: token in the header and the session
JSON in the body.
The correct 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 the Account
s the user
has some kind of permission, be it read or write.
Account
is what the frontend displays as
Group profile. For the purpose of
this documentation we are going to call
Account
because all of the endpoints and
naming are like that.
An Account
is basically an aggregation of
data that several users have access to.
Some other software may call it Group,
Organization, or Team.
Account
and User
are the only
two records that does not depend on anything to exists.
As you could see in the session output, the
roles
attribute exposes an array with the
unique ID
of each Account
the
user has permission.
GET /accounts/:account_id
This endpoint returns the JSON data for one
Account
. There is no way to request data for
more than one Account
at the same time.
curl \ -X GET \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1"
The output is:
{ "_id": "acc-1e36d77db5434f3cbded1bb07db403c1" }
As you can see there is not much. That is because our API
returns only the fields requested and if nothing is in
the list of fields it returns the _id
of the
record.
To get more data you can use the fields
query parameters:
curl \ -X GET \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1?fields=_id,_rev,name"
And it outputs:
{ "_id": "acc-1e36d77db5434f3cbded1bb07db403c1", "_rev": "9-2b48b88130cde7bec684dc546ca39f36", "name": "Simple Corp." }
_rev
is an very important field, take note of this.
You can see more fields in the link to the OpenAPI in the sidebar.
PUT /accounts/:account_id
This endpoint updates the data of one
Account
.
curl \ -X PUT \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1" \ -d '{"name": "Simple Co - Edit"}'
And it outputs:
{ "error": "conflict", "reason": "Document update conflict." }
To avoid two users editing the same resource we created a
locking mechanism similar to the one CouchDB uses. It is
a simple incremental per record integer followed for a
md5 sum of the record. Which means that in order to
update you need to first query the record and then send
the current _rev
.
Assuming you have the correct _rev
you can
send it as a query parameter:
curl \ -X PUT \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1?rev=9-2b48b88130cde7bec684dc546ca39f36" \ -d '{"name": "Simple Co - Edit"}'
And the output is now going to tell you the update was successful:
{ "id": "acc-1e36d77db5434f3cbded1bb07db403c1", "ok": true }
If you try to send an empty body or you try to update fields that are not there you are going to get the same reply and nothing is going to change.
All of the three example below produces the same result.
Example #1, request with empty JSON body:
curl \ -X PUT \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1?rev=11-83ae374f5748c1595e1505ce2cc3800b" \ -d '{}'
Example #2, request with no body:
curl \ -X PUT \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1?rev=12-83ae374f5748c1595e1505ce2cc3800b"
Example #3, request with a non-existent field body:
curl \ -X PUT \ "https://api.simpleserviceorder.com/accounts/acc-1e36d77db5434f3cbded1bb07db403c1?rev=13-83ae374f5748c1595e1505ce2cc3800b" \ -d '{"non-existent-field": true}'
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.
crystals
records, e.g., this
is an API-only feature.
This kind of record lets you manage
crystals
, each Crystal 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. Textage
: The age of the Crystal, the number stored in this field has to be lower than 125. Integername
field of
Crystal. To force an update you can delete
the Crystal and create a new one with the
same _id
.
POST /:account_id
When you sign up the _id
of your
Account and User are random, we do that
to prevent Accounts 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 /:account_id/: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" }
As you remember from the /accounts
endpoint
if you don't tell which fields you want in your request
we are going to reply only with _id
.
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_at": "2023-05-13T23:16:24.792Z", "updated_at": "2023-05-13T23:16:24.792Z" }
PUT /:account_id/:document_id
The same rules you saw with updating using the
/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 /:account_id/: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 /:account_id/_find