Retrieve calls all returned 400 until I fixed my request body (query vs message); anything else like this I should check?

Sharing this in case it’s useful to someone else, and so someone can tell me if I’m still holding it wrong.

I’ve got four agents running on separate VMs, each doing Gmail ingestion into its own GoodMem space and then searching that space from cron jobs. The search side had been broken since deployment and I didn’t catch it: every retrieve call came back 400 Bad Request, the agents silently fell back to live Gmail searches with no memory context, and the output looked plausible enough that nothing jumped out.

When I finally dug in, the problem was my request body, not GoodMem. I was sending query when the API wants message, and maxResults where it wants requestedSize. I also wasn’t passing spaceKeys at all (I now send an empty array and let our proxy inject the right space). Once the fields matched, the response came back as NDJSON rather than a single JSON object, so I had to parse it line by line and pull out the memoryDefinition entries instead of doing one json.loads on the whole body.

After those changes retrieve returns 20 results from a space that’s ingested email, so it’s working. That was 15 cron prompts across 4 VMs with the wrong field names, which is entirely on me.

Two questions:

  1. Is spaceKeys: [] plus a proxy-injected space a reasonable pattern, or should each agent pass its space key explicitly?
  2. Are there other fields or response shapes in the retrieve/ingest endpoints that commonly trip people up the way query vs message did? I’d rather find them now than after another week of silent 400s.
1 Like

Hi Draymond! Thanks for making this post. Please allow me to chime in.

TLDR: use our SDKs, use spaceKeys, make sure you retrieve memories properly.

  1. An empty spaceKeys value is not supported. A proxy-injected space can definitely work. But in general it is best to pass in a spaceKey explicitly. Less moving parts that way! And it will help the queries stay self-contained.

  2. There are some important points to know about your current approach:

memoryDefinition isn’t the results event. It ships each parent memory exactly once so you can cache it and join by memoryIndex, rather than us resending a whole document with every chunk. The ranked hits arrive as retrievedItem, and that’s where chunkText and relevanceScore live. So if you’re only reading memoryDefinition, you’re getting a deduplicated set of documents with no scores instead of your actual ranked results. Please use retrievedItem for ranked hits. The base vector stage ranks by distance, so lower is better. A reranker returns conventional relevance, where higher is better.

Also, if you want the raw memory content inline, add fetchMemoryContent: true to your request, and pass fetchMemory: true explicitly alongside it. Or pull it on demand from GET /v1/memories/{id}/content

Finally, you may want to keep in mind that ingest happens asynchronously in Goodmem. When you POST a memory, the chunking and embedding run as a background job, so you get back processingStatus: PENDING and it isn’t searchable yet. If you want to search a memory immediately after posting it, you’ll want to poll GET /v1/memories/{id} until it reaches COMPLETED (or FAILED). If you don’t, a retrieve right after ingest may come back empty.

  1. It can be tricky at times to write HTTP requests directly. We have made example HTTP requests available in the Goodmem documentation to help with this. But! Please consider using our SDKs. That is the recommended approach. We spent a lot of effort to make sure that folks don’t have to deal with direct HTTP requests and responses. Goodmem SDKs handle the NDJSON parsing, the event dispatch, and the field names for you. pip install goodmem or npm install @pairsystems/goodmem and you’re set. Go, Java, and .NET are published as well.
1 Like