How Hook Events Are Sent to the ai-memory Server and Response Codes Explained

When an agent triggers a lifecycle hook, the ai_memory_post_hook function in hooks/_lib.sh POSTs a JSON payload to the /hook endpoint with a 0.5-second timeout, and the ai-memory server returns HTTP 202 Accepted on success.

The akitaonrails/ai-memory repository implements a fire-and-forget observability system where agents report session lifecycle events through shell hooks. Understanding how these hook events are transmitted to the ai-memory server and which response codes indicate success or failure is essential for debugging integration issues without blocking agent execution.

Client-Side Hook Transmission

Hook events originate in agent lifecycle scripts (such as session-start, pre-tool-use, post-tool-use, and session-end) that source the shared library at hooks/_lib.sh. This library provides the ai_memory_post_hook function, which constructs and transmits the HTTP request to the ai-memory server.

Authentication and Headers

The ai_memory_post_hook function automatically handles authentication by checking for the AI_MEMORY_AUTH_TOKEN environment variable or a header file returned by ai_memory_auth_header_file. When a token is present, it adds an Authorization: Bearer header to the request. All requests include Content-Type: application/json to ensure proper payload parsing on the server side.

Request Construction and Timeout

To prevent hooks from blocking agent execution, the client enforces a strict 0.5-second timeout using curl's --max-time flag. The function accepts the target URL as the first argument and reads the JSON payload from stdin via --data-binary @-. This design allows the agent to fire events asynchronously without waiting for full server processing.


# From hooks/_lib.sh

ai_memory_post_hook() {
    _amhdr=$(ai_memory_auth_header_file)
    if [ -n "${AI_MEMORY_AUTH_TOKEN:-}" ]; then
        curl -s --max-time 0.5 -X POST "$1" \
            -H "Content-Type: application/json" \
            -H "Authorization: Bearer $AI_MEMORY_AUTH_TOKEN" \
            --data-binary @-
    elif [ -n "$_amhdr" ]; then
        curl -s --max-time 0.5 -X POST "$1" \
            -H "Content-Type: application/json" \
            -H @"$_amhdr" \
            --data-binary @-
    else
        curl -s --max-time 0.5 -X POST "$1" \
            -H "Content-Type: application/json" \
            --data-binary @-
    fi
}

The URL construction is handled by ai_memory_url_encode in the same file, which appends query parameters including event, agent, session_id, and contextual data like project and cwd when a marker file is detected.

Server-Side Processing and Response Codes

On the server side, the MCP router defined in crates/ai-memory-mcp/src/router.rs registers the POST /hook route and delegates handling to ai_memory_hooks::hook_router. This architecture separates routing logic from the core hook processing implementation.

The /hook Endpoint Handler

When the server receives a hook event, the handler parses the JSON payload, sanitizes the input, and persists the observation to the SQLite store. This operation is designed to be lightweight to accommodate the client's short timeout window. The implementation emphasizes durability over immediate confirmation, accepting the payload for asynchronous processing.

HTTP Response Codes and Their Meanings

The ai-memory server uses specific HTTP status codes to communicate the result of hook ingestion:

  • 202 Accepted: Returned when the payload is successfully received and queued for storage. This indicates the hook event was accepted by the server, though processing may continue asynchronously.
  • 400 Bad Request: Returned when the JSON payload is malformed or required fields are missing in the request body.
  • 500 Internal Server Error: Returned for unexpected internal failures during processing, such as database write errors or serialization issues.

The unit tests in crates/ai-memory-mcp/tests/suite/stress_autoscope.rs explicitly verify this contract:

// From crates/ai-memory-mcp/tests/suite/stress_autoscope.rs
let resp = client.request(hook_request(...)).await.unwrap();
assert_eq!(resp.status(), StatusCode::ACCEPTED, "hook must accept");

This test ensures that under load, the server consistently returns 202 Accepted for valid hook events, confirming the fire-and-forget semantics expected by the client implementation.

Practical Code Examples

You can send hook events directly using standard HTTP tools or programmatic clients. The examples below demonstrate the expected request format and how to verify the 202 Accepted response.

Sending Hooks with cURL

For debugging or custom integrations, you can replicate the client behavior using cURL with the same 0.5-second timeout used by the shell library:


# Example: Manual session-start hook submission

payload='{"event":"session-start","session_id":"abc123","cwd":"/home/user/project"}'
curl -s --max-time 0.5 -X POST \
     "http://localhost:49374/hook?event=session-start&agent=claude-code" \
     -H "Content-Type: application/json" \
     --data-binary "$payload"

# Expected: HTTP/1.1 202 Accepted

Rust Client Implementation

When integrating with Rust applications using reqwest, expect the 202 status code to confirm successful acceptance:

use reqwest::StatusCode;

async fn send_hook_event() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let resp = client
        .post("http://localhost:49374/hook?event=session-start&agent=claude-code")
        .header("Content-Type", "application/json")
        .body(r#"{"session_id":"abc123","cwd":"/home/user/project"}"#)
        .send()
        .await?;
    
    assert_eq!(resp.status(), StatusCode::ACCEPTED);
    Ok(())
}

Summary

  • Hook events are transmitted via ai_memory_post_hook in hooks/_lib.sh, which uses curl with a 0.5-second timeout to ensure non-blocking execution.
  • The client automatically injects Authorization: Bearer headers when AI_MEMORY_AUTH_TOKEN is set, or reads headers from the auth file helper.
  • Server-side routing in crates/ai-memory-mcp/src/router.rs handles the POST /hook endpoint through the ai_memory_hooks::hook_router handler.
  • Successful ingestion returns HTTP 202 Accepted, while malformed requests return 400 Bad Request and server errors return 500 Internal Server Error.
  • The response contract is verified by stress tests in crates/ai-memory-mcp/tests/suite/stress_autoscope.rs.

Frequently Asked Questions

What happens if the ai-memory server is offline when a hook event is sent?

The client-side curl command in hooks/_lib.sh uses a 0.5-second timeout (--max-time 0.5) and silent mode (-s), which means failed connections or timeouts will fail silently without blocking the agent. The hook event is lost, but the agent continues execution uninterrupted, maintaining the fire-and-forget design philosophy.

How does authentication work for hook events in ai-memory?

Authentication is handled by the ai_memory_post_hook function checking for the AI_MEMORY_AUTH_TOKEN environment variable first. If present, it adds Authorization: Bearer <token> to the request. If not, it falls back to checking the file returned by ai_memory_auth_header_file and includes those headers instead. If neither is available, the request proceeds without authentication headers, suitable for local development environments.

Why does the server return 202 Accepted instead of 200 OK for hook events?

The HTTP 202 Accepted status indicates that the server has received the hook event and queued it for processing, but has not yet completed persisting the observation to the SQLite store. This status code accurately represents the asynchronous nature of the ingestion pipeline and confirms that the client can safely disconnect after the timeout without waiting for full disk write confirmation.

What payload fields are required when sending hook events to ai-memory?

While the exact schema is validated by the server handler, the standard hook payload includes event (the lifecycle type), session_id (unique identifier), and cwd (current working directory). Query parameters appended to the URL include agent (identifier string) and event type. Missing required fields in the JSON body will result in a 400 Bad Request response according to the server's validation logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →