openapi: 3.2.0 info: title: 'GTM API public contract: gtm.service.id Media Uploads API' description: 'Identity, access and money: users, teams and members, API keys, OAuth clients and authorizations, billing products, subscriptions, transactions and payment methods, notifications, TLS certificates and support requests.' version: '1.0' contact: name: GTM API url: https://gtm-api.com email: support@gtm-api.com license: name: Proprietary url: https://gtm-api.com/license servers: - url: https://app.gtm-api.com/id/v4 description: Production, through the app.gtm-api.com gateway security: - BearerJwt: [] TeamSid: [] tags: - name: media_uploads description: Registry package `mcp.id/media_uploads`, served on MCP mount `id.platform`. paths: /api/media-uploads/request-upload: post: operationId: request_media_upload summary: Request media upload description: 'Get a place for ONE image or video file, then pass its file_url to create_linkedin_post (images[].url or video.url) instead of base64. In a chat without HTTP (the user pasted or has the picture): give the user upload_link, ask them to open it and drop the file, and call create_linkedin_post with file_url once they confirm. With a shell: run the curl line (upload_link, replace ). Pass file_type to also get a pre-signed S3 POST form (upload). Never type base64 of a pasted image: the model sees pixels, not the file, and the bytes come out wrong. The link works once for 30 minutes, up to 35 MB, images PNG/JPEG/GIF/WEBP or video MP4/MOV/WEBM (checked by content). The file is public by URL and deleted after 7 days. 120 per workspace per hour. Contract: - MCP tool `request_media_upload`, registry package `mcp.id/media_uploads`, mount `id.platform`. - Operation `action`, response envelope `action`. - Flags: none.' tags: - media_uploads requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/RequestMediaUploadRequest' responses: '200': description: '`action` success envelope.' content: application/json: schema: $ref: '#/components/schemas/RequestMediaUploadResponse' 4XX: $ref: '#/components/responses/McpClientError' 5XX: $ref: '#/components/responses/McpServerError' x-gtm-mcp-tool: request_media_upload x-gtm-mcp-mount: id.platform x-gtm-entity: media_uploads x-gtm-operation: action x-gtm-envelope: action x-gtm-availability: ga x-gtm-dangerous: false x-gtm-mass-action: false x-gtm-step-eligible: false x-gtm-schedule-required: false x-gtm-tool-class: complex components: responses: McpServerError: description: MCP error envelope with `error.code` internal_error or service_unavailable. content: application/json: schema: $ref: '#/components/schemas/McpError' McpClientError: description: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented. content: application/json: schema: $ref: '#/components/schemas/McpError' schemas: McpError: type: object properties: success: type: boolean enum: - false error: type: object properties: code: type: string enum: - validation_failed - nothing_to_update - not_found - relation_not_found - invalid_transition - limit_exceeded - payment_required - duplicate_rejected - conflict - delete_blocked - unauthorized - forbidden - rate_limited - internal_error - service_unavailable - not_implemented message: type: string recoverable: type: boolean suggestion: type: string field_errors: type: object additionalProperties: type: array items: type: object properties: rule: type: string message: type: string required: - rule - message blockers: type: array items: type: object properties: type: type: string description: Machine-readable blocker type (active_flow, pending_tasks, …). severity: type: string enum: - hard - soft description: hard = external action required; soft = acknowledge is enough. description: type: string entity_sid: type: - string - 'null' count: type: integer resolution: type: string description: 'Hard: tool name to call. Soft: code for acknowledge[].' resolution_hint: type: string required: - type - severity - description - entity_sid - resolution - resolution_hint context: type: object additionalProperties: {} required: - code - message - recoverable meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms required: - success - error RequestMediaUploadResponse: type: object properties: success: type: boolean enum: - true operation: type: string enum: - action action: type: string description: kebab-case verb; matches the route segment. item: {} result: type: object properties: purpose: type: string description: What the slot is for (post_media today); it decides the accepted file types. upload_link: type: string description: One-time link on our domain, valid until upload_expires_at. A person opens it in a browser and drops the file; a shell sends it with the curl line. Accepts one successful upload. upload: type: - object - 'null' properties: url: type: string description: The S3 form action to POST to. method: type: string enum: - POST enctype: type: string enum: - multipart/form-data fields: type: object additionalProperties: type: string description: Send EVERY entry verbatim as a form field, before the file. file_field: type: string enum: - file description: The form field that carries the bytes; it must be the LAST part of the form. required: - url - method - enctype - fields - file_field description: A pre-signed S3 POST form for the same object, only when file_type was given; null otherwise. file_url: type: string description: 'The object''s public https URL once uploaded: pass it as images[].url or video.url.' upload_expires_at: type: string description: 'ISO 8601: the form refuses uploads after this.' retained_until: type: string description: 'ISO 8601: the object is deleted after this.' max_byte_size: type: integer description: The largest file the form accepts, in bytes. curl: type: string description: 'A ready upload command through upload_link: replace with the local file path. Answers 201 with the stored type and size.' required: - purpose - upload_link - upload - file_url - upload_expires_at - retained_until - max_byte_size - curl meta: type: object properties: trace_id: type: string description: UUID v7; same 128-bit value as the X-Trace-Id header. span_id: type: string pattern: ^[0-9a-f]{16}$ description: 16 hex chars, root span of this request. timestamp: type: string description: ISO 8601 UTC (Y-m-dTH:i:sZ), response time. duration_ms: type: integer minimum: 0 description: Server-side wall clock. team_sid: type: - string - 'null' description: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends. actor_type: type: - string - 'null' description: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends. required: - trace_id - span_id - timestamp - duration_ms required: - success - operation - action - item - result - meta RequestMediaUploadRequest: type: object description: Request body of `request_media_upload`. properties: purpose: type: string enum: - post_media description: 'What the file is for. post_media (the default): an image or video for create_linkedin_post.' file_name: type: string minLength: 1 maxLength: 200 description: Optional file name, e.g. "q3-chart.png". Characters outside A-Z a-z 0-9 . _ - become "-" in the stored name. Omit when you do not know it yet. file_type: type: string enum: - image/png - image/jpeg - image/gif - image/webp - video/mp4 - video/quicktime - video/webm description: 'Optional MIME type, one the purpose takes. Given, it also mints the pre-signed S3 form with this Content-Type bound in. The upload_link needs no type: it reads it from the file.' securitySchemes: BearerJwt: type: http scheme: bearer bearerFormat: JWT description: Access token issued by gtm.service.id. Its `access_identity` claim carries `team_sid`, `actor_sid` and `actor_type`, and that team scope is authoritative. TeamSid: type: apiKey in: header name: Team-SID description: Team scope for tokens that do not carry one. Ignored when the token already names a team.