Skip to main content
Glama

submit_receipt

Submit a photo or PDF of a receipt for processing. Covers 'log this', 'save this receipt', 'expense this', 'add this to my expenses', including when the user simply shares a photo of a receipt or invoice. The file is validated, uploaded and processed by AI to extract vendor, amount, date, tax and category. The expense appears in the spreadsheet in about 1-3 minutes, longer for PDFs or large batches. Handles images and PDFs mixed in one batch.

TO SEND FILES (preferred, and required for PDFs): call this tool with filesToUpload listing every file the user gave you. It returns one signed upload URL per file. Upload them ONE AT A TIME with an HTTP PUT, telling the user which file you just finished and how many remain, then call this tool ONCE with uploadRefs for all of them to process the whole set as a single batch. Do not call this tool once per file.

Use the photo parameter for one image or PDF attached in ChatGPT. Clients that cannot supply file references may use photoBase64 for one small image; use the upload flow for large files or batches.

Optional note and tag use the same metadata path as ExpenseBot's camera, uploader and forwarded-email intake: note goes to Notes (column L), tag to Tag (column K). Batch defaults apply to every file; each uploadRefs item may override either value.

Input Schema

NameRequiredDescriptionDefault
tagNoBatch-level project or client tag for every row; a file may override it. Stored in the Tag column (K).
noteNoBatch-level note for every file; a file may override it. Stored in the Notes column (L).
photoNoSingle attached receipt image or PDF supplied by ChatGPT. For multiple files or large files, use filesToUpload and uploadRefs instead.
filenameNoOptional filename (e.g., 'lunch_receipt.jpg')
mimeTypeNoMIME type of the file (default: image/jpeg)
uploadRefNoSingle-file shorthand for uploadRefs. Use uploadRefs when there is more than one file.
uploadRefsNoSubmit previously uploaded files as ONE batch: every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed.
photoBase64NoLegacy fallback for MCP clients that send one small image as complete base64 data. ChatGPT should use photo or uploadRefs instead.
filesToUploadNoRequest upload URLs for every file the user provided, in one call. Returns a signedUrl + uploadRef per file; upload each, then call again with uploadRefs.

Output Schema

NameRequiredDescriptionDefault
dataNo
filesNo
statusNo
messageNo
successYes
fileCountNo
statusCodeNo
submissionIdNo
spreadsheetUrlNo
reviewExpensesUrlNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed6 schema fields changed
    • changedInput schema / properties / filesToUpload / description
      Previous value: -"Request upload URLs for one or more files. Include EVERY file the user provided in a single call. Returns one signedUrl + uploadRef per file; upload each, then call this tool again with uploadRefs."New value: +"Request upload URLs for every file the user provided, in one call. Returns a signedUrl + uploadRef per file; upload each, then call again with uploadRefs."
    • changedInput schema / properties / filesToUpload / items / properties / mimeType / description
      Previous value: -"MIME type. Must match the Content-Type header used in the PUT exactly. The real format is re-detected from the file's contents on submit, so a close guess still works."New value: +"MIME type; must match the PUT Content-Type exactly. The real format is re-detected from the contents."
    • changedInput schema / properties / note / description
      Previous value: -"Batch-level context note applied to every file in the upload. Individual files may override this with their own note. Stored through the existing receipt-note pipeline in the Notes column (L)."New value: +"Batch-level note for every file; a file may override it. Stored in the Notes column (L)."
    • changedInput schema / properties / tag / description
      Previous value: -"Batch-level project or client tag applied to every expense row. Individual files may override it. Stored in the Tag column (K) through the same path as the web receipt uploader."New value: +"Batch-level project or client tag for every row; a file may override it. Stored in the Tag column (K)."
    • changedInput schema / properties / uploadRefs / description
      Previous value: -"Submit previously uploaded files as ONE batch. Include every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed."New value: +"Submit previously uploaded files as ONE batch: every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed."
    • changedInput schema / properties / uploadRefs / items / properties / note / description
      Previous value: -"Context note for this specific file. Overrides the batch-level note if both are provided. Uses the existing receipt-note pipeline and is stored in the Notes column (L)."New value: +"Note for this file; overrides the batch note. Stored in the Notes column (L)."
  2. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "additionalProperties": true,
      +  "allOf": [
      +    {
      +      "if": {
      +        "properties": {
      +          "status": {
      +            "const": "upload_required"
      +          }
      +        },
      +        "required": [
      +          "status"
      +        ]
      +      },
      +      "then": {
      +        "required": [
      +          "data"
      +        ]
      +      }
      +    }
      +  ],
      +  "properties": {
      +    "data": {
      +      "additionalProperties": true,
      +      "properties": {
      +        "expiresInSeconds": {
      +          "minimum": 1,
      +          "type": "integer"
      +        },
      +        "files": {
      +          "items": {
      +            "additionalProperties": true,
      +            "properties": {
      +              "filename": {
      +                "type": "string"
      +              },
      +              "id": {
      +                "type": "string"
      +              },
      +              "mimeType": {
      +                "type": "string"
      +              },
      +              "requiredHeaders": {
      +                "additionalProperties": {
      +                  "type": "string"
      +                },
      +                "type": "object"
      +              },
      +              "signedUrl": {
      +                "type": "string"
      +              },
      +              "uploadRef": {
      +                "type": "string"
      +              }
      +            },
      +            "type": "object"
      +          },
      +          "type": "array"
      +        },
      +        "requiredHeaders": {
      +          "additionalProperties": {
      +            "type": "string"
      +          },
      +          "type": "object"
      +        },
      +        "reviewExpensesUrl": {
      +          "type": "string"
      +        },
      +        "signedUrl": {
      +          "type": "string"
      +        },
      +        "spreadsheetUrl": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "submissionId": {
      +          "type": "string"
      +        },
      +        "uploadRef": {
      +          "type": "string"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "fileCount": {
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "files": {
      +      "items": {
      +        "additionalProperties": true,
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "message": {
      +      "type": "string"
      +    },
      +    "reviewExpensesUrl": {
      +      "type": "string"
      +    },
      +    "spreadsheetUrl": {
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    },
      +    "status": {
      +      "type": "string"
      +    },
      +    "statusCode": {
      +      "type": "integer"
      +    },
      +    "submissionId": {
      +      "type": "string"
      +    },
      +    "success": {
      +      "type": "boolean"
      +    }
      +  },
      +  "required": [
      +    "success"
      +  ],
      +  "type": "object"
      +}
  3. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -{
      -  "additionalProperties": true,
      -  "description": "Standard ExpenseBot tool result envelope. `message` is the human-readable summary the AI cites; `data` is the structured payload (totals, breakdowns, ids, etc.). On failure, `success` is false and `error` carries a code/message/hint triple.",
      -  "properties": {
      -    "data": {
      -      "additionalProperties": true,
      -      "description": "Structured payload. Shape varies per tool — common keys: total, breakdown, comparison, sampleMeta, ids, expenseId, reportId, signupUrl, results.",
      -      "type": "object"
      -    },
      -    "error": {
      -      "additionalProperties": true,
      -      "description": "Present only when success === false.",
      -      "properties": {
      -        "code": {
      -          "type": "string"
      -        },
      -        "hint": {
      -          "type": "string"
      -        },
      -        "message": {
      -          "type": "string"
      -        }
      -      },
      -      "type": "object"
      -    },
      -    "message": {
      -      "description": "Human-readable result text. Always present on success; prefer rendering this verbatim before any further reasoning.",
      -      "type": "string"
      -    },
      -    "sampleMeta": {
      -      "additionalProperties": true,
      -      "description": "Set when the underlying dataset was truncated. isTruncated=true means the agent saw a sample of `sampleCount` of `totalCount` rows; aggregate totals are still accurate.",
      -      "properties": {
      -        "isTruncated": {
      -          "type": "boolean"
      -        },
      -        "sampleCount": {
      -          "type": "integer"
      -        },
      -        "totalCount": {
      -          "type": "integer"
      -        }
      -      },
      -      "type": "object"
      -    },
      -    "success": {
      -      "description": "False on tool errors; check before reading `data`.",
      -      "type": "boolean"
      -    }
      -  },
      -  "type": "object"
      -}New value: +null
  4. Changed5 schema fields changed
    • changedInput schema / properties / photo / description
      Previous value: -"Base64-encoded image or PDF data (JPEG, PNG, HEIC, WebP, or PDF). Max 15MB. Only for small images — large payloads get truncated or dropped in transit; use uploadRef instead."New value: +"Single attached receipt image or PDF supplied by ChatGPT. For multiple files or large files, use filesToUpload and uploadRefs instead."
    • addedInput schema / properties / photo / properties
      Added value: +{
      +  "download_url": {
      +    "description": "Temporary HTTPS URL supplied by ChatGPT for downloading the attachment.",
      +    "format": "uri",
      +    "type": "string"
      +  },
      +  "file_id": {
      +    "description": "Attachment identifier supplied by ChatGPT.",
      +    "type": "string"
      +  },
      +  "file_name": {
      +    "description": "Original attachment filename supplied by ChatGPT.",
      +    "type": "string"
      +  },
      +  "mime_type": {
      +    "description": "Attachment MIME type supplied by ChatGPT.",
      +    "type": "string"
      +  }
      +}
    • addedInput schema / properties / photo / required
      Added value: +[
      +  "download_url",
      +  "file_id"
      +]
    • changedInput schema / properties / photo / type
      Previous value: -"string"New value: +"object"
    • addedInput schema / properties / photoBase64
      Added value: +{
      +  "description": "Legacy fallback for MCP clients that send one small image as complete base64 data. ChatGPT should use photo or uploadRefs instead.",
      +  "type": "string"
      +}
  5. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=false, destructiveHint=false, openWorldHint=true. The description adds real context beyond that: AI extraction fields, a 1-3 minute latency expectation that is longer for PDFs/batches, atomic batch rejection, column destinations (K/L), and shared metadata path with camera/uploader/email intake.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and trigger phrases, then the file-transfer mechanics, then parameter nuances. It is longer than most definitions but nearly every sentence carries operational information; the parenthetical column letters and 'same metadata path as ExpenseBot's camera' line are slightly incidental.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter, nested-object, multi-step mutation with an output schema, the description covers the full workflow, timing expectations, failure atomicity, and mode selection. An agent has everything needed to invoke it correctly on the first try.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description genuinely adds semantics the schema cannot express: the two-phase upload workflow, that tag/note are batch defaults overridable per uploadRef item, and which physical spreadsheet columns they land in.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('submit a photo or PDF of a receipt for processing') and immediately enumerates the user intents it covers ('log this', 'save this receipt', 'expense this'). An agent can distinguish this from siblings like add_cash_expense or parse_expense without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes between modes: filesToUpload/uploadRefs is 'preferred, and required for PDFs'; photo for a single ChatGPT attachment; photoBase64 only as legacy fallback. It also gives an exclusion ('Do not call this tool once per file') and the required two-step order, leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.