JSON API
The JSON API provides an HTTP/REST interface to the Canton Ledger API. In Canton 3.x, it is integrated directly into the participant node and translates JSON HTTP requests into gRPC Ledger API calls.
The full reference consists of the OpenAPI and AsyncAPI specifications.
Other submission endpoints include
The OpenAPI and AsyncAPI specifications are available at runtime:
The configurable options that can be specified via config file include:
The token must be valid for the OIDC provider configured for your deployment.
A request body that cannot be decoded is answered with 400 Bad Request.
If a client’s HTTP GET or POST request reaches an API endpoint, the corresponding response contains a JSON object. Either an expected message (corresponding to endpoint) or an error object specified as in the example below:
Where:
Quick Reference
| Property | Value |
|---|---|
| Protocol | HTTP/1.1 + JSON |
| Port | 7575 in sandbox and LocalNet; set by http-ledger-api.port, no built-in default |
| Underlying API | Ledger API v2 (gRPC) |
| Authentication | JWT Bearer tokens |
When to Use the JSON API
The JSON API is the recommended access method when:- You’re building browser-based frontends or web applications
- Your language/framework works better with HTTP/JSON than gRPC
- You want simpler tooling for development and debugging (curl, Postman, etc.)
- You’re using the TypeScript/JavaScript Wallet SDK
- You need maximum performance for high-throughput streaming
- You’re using Java and want the native gRPC experience
- You need access to gRPC-specific features not exposed via JSON
Key Endpoints
Version Check
curl http://localhost:7575/v2/version
Command Submission
curl localhost:7575/v2/commands/submit-and-wait \
-H "Content-Type: application/json" \
-d '{
"commands": [
{
"CreateCommand": {
"createArguments": {
"issuer": "<partyId>",
"owner": "<partyId>",
"name": "Example Asset Name"
},
"templateId": "#json-tests:Main:Asset"
}
}
],
"userId": "ledger-api-user",
"commandId": "example-app-create-1234",
"actAs": ["<partyId>"],
"readAs": ["<partyId>"]
}'
/v2/commands/submit-and-wait-for-transaction (returns the full transaction) and /v2/commands/async/submit (returns immediately).
Active Contract Queries
curl localhost:7575/v2/state/active-contracts \
-H "Content-Type: application/json" \
-d '{
"eventFormat": {
"filtersByParty": {},
"filtersForAnyParty": {
"cumulative": [
{
"identifierFilter": {
"WildcardFilter": {
"value": {
"includeCreatedEventBlob": true
}
}
}
}
]
},
"verbose": false
},
"activeAtOffset": 20
}'
Transaction Streaming
The JSON API supports both HTTP POST and WebSocket connections for streaming. The WebSocket channels are listed in the AsyncAPI reference. For WebSocket requests, you must pass two subprotocols:jwt.token.<paste-jwt-here>daml.ws.auth
wscat:
wscat -c http://localhost:7575/v2/state/active-contracts \
-s "jwt.token.<paste-jwt-here>" \
-s "daml.ws.auth"
Ledger End
GET /v2/state/ledger-end returns the latest absolute offset on the participant. Use it as the activeAtOffset value when querying the ACS to get the most recent state, or pass an older non-pruned offset to read state at a historical point.
Interactive Submission
External-party command submission goes through two endpoints rather than the standardsubmit-and-wait flow:
POST /v2/interactive-submission/prepare— produces a prepared transaction the external party signs off-participant.POST /v2/interactive-submission/execute— submits the signed prepared transaction back to the participant for ledger execution.
Configuration
The JSON API is configured via thehttp-ledger-api section of the participant node’s configuration. In cn-quickstart LocalNet deployments, it is pre-configured and available at http://localhost:7575.
Never expose the JSON API to the Internet. In production, run it behind a reverse proxy such as NGINX.
curl http://localhost:7575/docs/openapi
curl http://localhost:7575/docs/asyncapi
canton {
participants {
participant1 {
http-ledger-api {
// IP address that JSON Ledger API service listens on. Defaults to 127.0.0.1.
address = "127.0.0.1"
// JSON Ledger API service port number.
port = 7575
// Prefix added to all JSON endpoints.
path-prefix = "example/prefix"
websocket-config {
// Maximum number of elements returned when using the HTTP POST alternative.
http-list-max-elements-limit = 1024
// Wait time for new elements before returning the list via the HTTP POST alternative.
http-list-wait-time = "1s"
}
}
}
}
}
Your JSON Ledger API service should never be exposed to the Internet. When running in production the JSON Ledger API should be behind a reverse proxy, such as via NGINX.
Authentication
Each request to the JSON Ledger API must come with an access token (JWT). The JSON Ledger API does not hold on to the access token, which will be only used to fulfill the request it came along with. The same token will be used to issue the request to the Ledger API. The exceptions are the documentation endpoints (/docs/openapi, /docs/asyncapi), /v2/version, and the health endpoints (/livez, /readyz), which do not require a token.
For a reference on the JWT tokens we use, please read Authorization.
Auth via HTTP
Pass a JWT Bearer token in theAuthorization header:
curl -H "Authorization: Bearer <token>" http://localhost:7575/v2/...
Auth via WebSockets
WebSocket clients support a “subprotocols” argument (sometimes simply called “protocols”); this is usually in a list form but occasionally in comma-separated form. Check the documentation for your WebSocket library of choice for details. For WebSocket requests, you must pass two subprotocols:daml.ws.authjwt.token.<paste-jwt-here>
wscat:
wscat -c http://localhost:7575/v2/state/active-contracts \
-s "jwt.token.<token>" \
-s "daml.ws.auth"
Errors
The JSON Ledger API reports errors using standard HTTP status codes. When the gRPC Ledger API returns an error code, the JSON Ledger API maps it to an HTTP status code:| gRPC code | HTTP status |
|---|---|
INVALID_ARGUMENT, FAILED_PRECONDITION, OUT_OF_RANGE | 400 Bad Request |
UNAUTHENTICATED | 401 Unauthorized |
PERMISSION_DENIED | 403 Forbidden |
NOT_FOUND | 404 Not Found |
ABORTED, ALREADY_EXISTS | 409 Conflict |
RESOURCE_EXHAUSTED | 429 Too Many Requests |
CANCELLED | 499 Client Closed Request |
INTERNAL, UNKNOWN, DATA_LOSS | 500 Internal Server Error |
UNIMPLEMENTED | 501 Not Implemented |
UNAVAILABLE | 503 Service Unavailable |
DEADLINE_EXCEEDED | 504 Gateway Timeout |
{
"cause" : "The submitted request has invalid arguments: Cannot unassign contracts `List(ContractId(00a7feb291fe1be6289c4d77f3a432b623083ed3f49bf8535f8571aa8bdf68b647ca10122006207a71ac6ed62dae27429b43e456dfbbc411a49d67b7e0067bef4c914c6e8a))`: source and target synchronizers are the same",
"code" : "INVALID_ARGUMENT",
"context" : {
"category" : "8",
"definite_answer" : "false",
"participant" : "participant1",
"test" : "JsonV2Tests",
"tid" : "99e67812af315256ef9a02a9fdb94646"
},
"correlationId" : null,
"definiteAnswer" : null,
"errorCategory" : 8,
"grpcCodeValue" : 3,
"resources" : [ ],
"retryInfo" : null,
"traceId" : "99e67812af315256ef9a02a9fdb94646"
}
cause— a textual message containing readable error reason,code— a Ledger API error code,context— a Ledger API context of an error,traceId— telemetry tracing id,grpcCodeValueanderrorCategory— defined in Error Codes.
WebSockets Errors
In the case of WebSockets an error might be delivered as a frame. Each incoming frame can either be a correct response (corresponding to the endpoint definition) or an error frame in the format above.Related Pages
- Ledger API — gRPC Ledger API overview
- Ledger API Reference (AppDev) — Detailed service documentation
- Wallet Configuration — SDK configuration including JSON API endpoints