Skip to content

2. Send your first JMAP request

You've got a server running and a bearer token. Time to talk JMAP.

The shape of a JMAP request

Every JMAP call is a POST to /jmap with this envelope (RFC 8620 §3.3):

{
  "using": ["urn:ietf:params:jmap:core"],
  "methodCalls": [
    ["Core/echo", {"hello": "world"}, "c0"]
  ]
}
  • using — the capability URNs your request touches. The server rejects the request if it doesn't advertise one of them.
  • methodCalls — an array of [methodName, args, callId] triples. Calls in the same request can reference each other's results (see step 3).

The response mirrors the request:

{
  "methodResponses": [
    ["Core/echo", {"hello": "world"}, "c0"]
  ],
  "sessionState": "9e1f2c…"
}

Try it

TOKEN='chl_…'
curl -s -X POST http://localhost:8000/jmap \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "using": ["urn:ietf:params:jmap:core"],
    "methodCalls": [["Core/echo", {"hi": "there"}, "c0"]]
  }' | jq

Core/echo is the simplest JMAP method — it returns its arguments verbatim. Useful for testing connectivity and for examples below.

Discover what the server can do

GET /.well-known/jmap returns the session resource. It tells you:

  • which capabilities are enabled (look at capabilities)
  • which accounts your token can reach (accounts)
  • the URLs to use for upload / download / push (apiUrl, uploadUrl, …)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8000/.well-known/jmap | jq .capabilities

You'll see the four bundled capabilities:

{
  "urn:ietf:params:jmap:core": {  },
  "urn:ietf:params:jmap:websocket": {  },
  "urn:ietf:params:jmap:blob": {  },
  "urn:jmaple:notes": {  }
}

Talk to the reference notes capability

ACCOUNT_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/.well-known/jmap | jq -r '.accounts | keys[0]')

curl -s -X POST http://localhost:8000/jmap \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{
    \"using\": [\"urn:ietf:params:jmap:core\", \"urn:jmaple:notes\"],
    \"methodCalls\": [
      [\"Note/set\", {
        \"accountId\": \"$ACCOUNT_ID\",
        \"create\": {\"n1\": {\"title\": \"Hello\", \"body\": \"world\"}}
      }, \"c0\"]
    ]
  }" | jq

You should see a created object mapping your n1 create-id to the server-assigned id, plus the new sessionState.

Now you've used the framework. Time to add your own capability → Writing your first capability