LangGraph Sharing
This guide shows how to add conversation sharing and ownership transfer APIs to the LangGraph tutorial app.
New to sharing concepts? Read Sharing & Access Control first to understand access levels and transfer flow.
Prerequisites
Starting checkpoint: This guide starts from python/examples/langgraph/doc-checkpoints/03-with-history
Make sure you’ve completed:
- LangGraph Conversation History - History recording and conversation APIs
- Multiple users in Keycloak (
bob,alice,charlie)
Also complete Step 2 in LangGraph Dev Setup (build local memory-service-langchain wheel + UV_FIND_LINKS); this is temporary until the package is released.
Membership Endpoints
Checkpoint 06 adds membership endpoint passthroughs to the tutorial app:
@app.get("/v1/conversations/{conversation_id}/entries")
async def get_entries(conversation_id: str, request: Request):
response = await proxy.list_conversation_entries(
conversation_id,
after_cursor=request.query_params.get("afterCursor"),
limit=int(limit) if (limit := request.query_params.get("limit")) is not None else None,
channel="history",
)
return to_fastapi_response(response)
@app.get("/v1/conversations")
async def list_conversations(request: Request):
response = await proxy.list_conversations(
mode=request.query_params.get("mode"),
after_cursor=request.query_params.get("afterCursor"),
limit=int(limit) if (limit := request.query_params.get("limit")) is not None else None,
query=request.query_params.get("query"),
)
return to_fastapi_response(response)
@app.get("/v1/conversations/{conversation_id}/memberships") What changed: GET /v1/conversations/{conversation_id}/memberships, POST /v1/conversations/{conversation_id}/memberships, PATCH /v1/conversations/{conversation_id}/memberships/{user_id}, and DELETE /v1/conversations/{conversation_id}/memberships/{user_id} are added. Each delegates to the corresponding proxy.*_membership method.
Why: These endpoints expose Memory Service’s access-control layer to frontend clients. MemoryServiceProxy forwards each call with both the agent’s API key and the authenticated user’s bearer token so Memory Service can enforce that only conversation owners or managers may modify membership. The agent app does not implement any membership logic itself — it simply proxies the call through.
Ownership Transfer Endpoints
Checkpoint 06 also exposes ownership transfer APIs:
return to_fastapi_response(response)
@app.post("/v1/conversations/{conversation_id}/memberships")
async def create_membership(conversation_id: str, request: Request):
response = await proxy.create_membership(conversation_id, await parse_optional_json(request) or {})
return to_fastapi_response(response)
@app.patch("/v1/conversations/{conversation_id}/memberships/{user_id}")
async def update_membership(conversation_id: str, user_id: str, request: Request):
response = await proxy.update_membership(
conversation_id,
user_id,
await parse_optional_json(request) or {},
)
return to_fastapi_response(response)
@app.delete("/v1/conversations/{conversation_id}/memberships/{user_id}")
async def delete_membership(conversation_id: str, user_id: str):
response = await proxy.delete_membership(conversation_id, user_id)
return to_fastapi_response(response) What changed: GET /v1/ownership-transfers, POST /v1/ownership-transfers, POST /v1/ownership-transfers/{transfer_id}/accept, and DELETE /v1/ownership-transfers/{transfer_id} are added, all forwarded through proxy.*_ownership_transfer methods.
Why: Ownership transfer is a two-step operation: the current owner initiates a transfer, and the recipient accepts or declines it. Exposing these endpoints through the agent app means frontend clients can manage the full transfer lifecycle using the user’s bearer token, without needing direct access to the Memory Service admin API.
Testing Membership Management
Define a token helper for multiple users:
function get-token() {
local username=${1:-bob}
local password=${2:-$username}
curl -sSfX POST http://localhost:8081/realms/memory-service/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=memory-service-client" \
-d "client_secret=change-me" \
-d "grant_type=password" \
-d "username=$username" \
-d "password=$password" \
| jq -r '.access_token'
}
Create a conversation as bob:
curl -sSfX POST http://localhost:9090/chat/466db939-7218-4d03-9e31-810b134c063a \
-H "Authorization: Bearer $(get-token bob bob)" \
-H "Content-Type: text/plain" \
-d "Hello, starting a sharing test conversation." Example output:
I am a Python memory-service demo agent. List memberships:
curl -sSfX GET http://localhost:9090/v1/conversations/466db939-7218-4d03-9e31-810b134c063a/memberships \
-H "Authorization: Bearer $(get-token bob bob)" | jq Example output:
{
"afterCursor": null,
"data": [
{
"conversationId": "466db939-7218-4d03-9e31-810b134c063a",
"userId": "bob",
"accessLevel": "owner",
"createdAt": "2026-03-06T14:59:15.939792Z"
}
]
} Add alice as a writer:
curl -sSfX POST http://localhost:9090/v1/conversations/466db939-7218-4d03-9e31-810b134c063a/memberships \
-H "Authorization: Bearer $(get-token bob bob)" \
-H "Content-Type: application/json" \
-d '{"userId":"alice","accessLevel":"writer"}' | jq Example output:
{
"conversationId": "466db939-7218-4d03-9e31-810b134c063a",
"userId": "alice",
"accessLevel": "writer",
"createdAt": "2026-03-06T14:59:16.159763Z"
} Testing Ownership Transfers
Initiate an ownership transfer:
curl -sSfX POST http://localhost:9090/v1/ownership-transfers \
-H "Authorization: Bearer $(get-token bob bob)" \
-H "Content-Type: application/json" \
-d '{
"conversationId": "466db939-7218-4d03-9e31-810b134c063a",
"newOwnerUserId": "alice"
}' | jq
List transfers for the recipient:
curl -sSfX GET "http://localhost:9090/v1/ownership-transfers?role=recipient" \
-H "Authorization: Bearer $(get-token alice alice)" | jq
Accept a transfer:
curl -sSfX POST http://localhost:9090/v1/ownership-transfers/<transfer-id>/accept \
-H "Authorization: Bearer $(get-token alice alice)"
Decline or cancel a transfer:
curl -sSfX DELETE http://localhost:9090/v1/ownership-transfers/<transfer-id> \
-H "Authorization: Bearer $(get-token alice alice)"
Completed Checkpoint
Completed code: View the full implementation at python/examples/langgraph/doc-checkpoints/06-sharing
What’s Next
You’ve now added collaborative features (memberships and ownership transfer) to your LangGraph tutorial app.
Next, continue to Indexing and Search to add searchable indexed content.