Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.
Every Portal.io API request must include an X-MSS-SIGNATURE header containing a Base64-encoded HMAC-SHA256 signature. The signature is computed from a canonical message you build from properties of the request itself, then signed using your Secret Key. This page explains exactly how to construct the canonical message and compute the signature.
The canonical message is a single string formed by concatenating these components with no separator:For GET requests:
[HTTP method][base URL][timestamp][user API key]
For POST, PUT, and other non-GET requests:
[HTTP method][base URL][content type][timestamp][user API key]
The components map to your request as follows:
HTTP method — uppercase, e.g. GET, POST, PUT
Base URL — the scheme, host, and path only. Do not include query parameters. For example, use https://api.portal.io/public/proposals even if the actual request URL has ?PageNumber=1&PageSize=10 appended.
Content type — the exact value of the Content-Type header. Include this segment only for non-GET requests. The value differs per endpoint, so take it from that endpoint’s reference page rather than assuming one. Whatever you send, the content type in your signing string must match the Content-Type header exactly, or the request fails with 401. For multipart/form-data the signing string must include the full boundary=<value> parameter, not just multipart/form-data — see Sign multipart requests below.
Timestamp — the exact value you send in X-MSS-CUSTOM-DATE
User API key — the exact value you send in X-MSS-API-USERKEY
Use the base URL without query parameters. This is by design — query parameters are sent in the request as normal, but the server intentionally excludes them from signature verification. Only the scheme, host, and path are signed.
For GET requests, omit the content-type segment completely. Do not include an empty string in its place.
For non-GET requests, include the exact Content-Type value from the request header. The value in the signing string and the value in the header must match exactly — including case and any suffixes (e.g. application/json, not Application/JSON).
The request body is not part of the canonical message. Only the content type is included, not the body itself.
The timestamp must exactly match the value in X-MSS-CUSTOM-DATE, character for character.
The user API key must exactly match the value in X-MSS-API-USERKEY, character for character.
For the initial credential exchange, the user API key is an empty string in both the header and the canonical message.
Do NOT Base64-decode the Secret Key before computing the HMAC. Use it exactly as provided — as raw ASCII bytes. Base64-decoding the key before use is a common mistake that produces an invalid signature.
The timestamps in the canonical-message examples below are illustrative. For a real request, generate the current UTC timestamp and use that exact value in both the canonical message and the X-MSS-CUSTOM-DATE header, as shown in the full request example.
Base URL: https://api.portal.io/authenticate/apikeyexchange
Content type: (omitted — this is a GET request)
Timestamp: Mon, 06 Apr 2026 00:22:19 GMT
User API key: (empty string — this is the initial exchange)
Note that the actual HTTP request includes query parameters (?UserName=...&Password=...), but the canonical message uses only the base URL without them.
For multipart/form-data requests, the content type segment of the canonical message must include the full boundary=<value> parameter — the same value you send in the Content-Type header on the wire.For an upload to POST /public/api/proposals/{ProposalId}/ai/content, the canonical message looks like this:
User API key: qBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=
Use the same boundary string in both the Content-Type header you send and the canonical message you sign. Most HTTP clients generate a random boundary internally and don’t expose it. To work around this, either pre-build the multipart body and Content-Type yourself before signing, or use a client like Python’s requests-toolbelt.MultipartEncoder that lets you fix the boundary at construction time. The full request body (form fields and file parts) is still not included in the canonical message — only the content type with the boundary parameter is.
If a multipart endpoint is called with a query string (for example ?isMultiChunkUpload=true), the canonical message still uses the base URL without the query string, following the same rule as every other Portal.io endpoint.
This example generates the current UTC timestamp in RFC 7231 format. The same value is used in the canonical message and the X-MSS-CUSTOM-DATE header.Replace YOUR_APP_ID and YOUR_SECRET_KEY with your credentials. The X-MSS-API-USERKEY header is intentionally empty for this call.Once you have your User API Key, include it in both X-MSS-API-USERKEY and your canonical message on all subsequent requests.
{"docsConfig":{"theme":"palm","$schema":"https://mintlify.com/docs.json","name":"Portal.io API","colors":{"primary":"#FF5000","light":"#FF6B1A","dark":"#FF5000"},"logo":{"light":"https://mintcdn.com/portalio/QuSRLNwghuEPmEf4/logo-files/portal-logo-light.svg?fit=max\u0026auto=format\u0026n=QuSRLNwghuEPmEf4\u0026q=85\u0026s=b46857228d074b069b086559f1b032ab","dark":"https://mintcdn.com/portalio/QuSRLNwghuEPmEf4/logo-files/portal-logo-light.svg?fit=max\u0026auto=format\u0026n=QuSRLNwghuEPmEf4\u0026q=85\u0026s=b46857228d074b069b086559f1b032ab"},"favicon":"https://mintcdn.com/portalio/NuVzj4dQjIlaNLdz/favicon.svg?fit=max\u0026auto=format\u0026n=NuVzj4dQjIlaNLdz\u0026q=85\u0026s=98ab85efdca173dc9b3f2334c2437758","api":{"openapi":"public.json","playground":{"display":"interactive"},"examples":{"defaults":"all","languages":["bash","python","javascript","java","csharp"],"prefill":true}},"navbar":{"links":[],"primary":{"type":"button","label":"Contact Us","href":"https://portal.canny.io/developer-api"}},"fonts":{"heading":{"family":"Open Sans"},"body":{"family":"Open Sans"}},"contextual":{"options":["copy","view","claude","chatgpt","cursor","vscode","mcp"],"display":"header"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"portalio","hasAudioNarration":false,"actualSubdomain":"portalio","customDomains":["docs.portal.io"],"gitSource":{"type":"github","owner":"portalio","repo":"api-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6ad0dd3033278c5ac967f16f","collectionId":"ca37ba77-4a76-4cf1-a1b3-003273d4b6c0","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ad0dd3033278c5ac967f16f"},"entitlements":{"AI_CHAT":{"status":"ENABLED"}},"buildId":"6ad0dd3033278c5ac967f16f","clientVersion":"0.0.3965","searchFilterCounts":[{"language":"default","tag":"API Reference","count":59},{"language":"default","tag":"Proposal Items","count":19},{"language":"default","tag":"Guide","count":13},{"language":"default","tag":"Proposals","count":12},{"language":"default","tag":"AI Builder","count":6},{"language":"default","tag":"Pricing","count":6},{"language":"default","tag":"Concepts","count":6},{"language":"default","tag":"Get Started","count":5},{"language":"default","tag":"People","count":5},{"language":"default","tag":"Webhooks","count":5}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"support@portal.io","showHelpButton":true}},"hasChatPermissions":true,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":true},"customCss":[{"filePath":"custom.css","content":".sidebar-group-header,.sidebar-group-header h5#sidebar-title{text-transform:uppercase!important;letter-spacing:.05em!important}"}],"page":{"pagePath":"authentication/signing-requests","slug":"/authentication/signing-requests","pageMetadata":{"title":"Sign Portal.io API Requests with HMAC-SHA256","description":"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.","href":"/authentication/signing-requests"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,CodeGroup,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!CodeGroup)_missingMdxReference(`CodeGroup`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Every Portal.io API request must include an `,_jsx(_components.code,{children:`X-MSS-SIGNATURE`}),` header containing a Base64-encoded HMAC-SHA256 signature. The signature is computed from a canonical message you build from properties of the request itself, then signed using your Secret Key. This page explains exactly how to construct the canonical message and compute the signature.`]}),`\n`,_jsx(Heading,{level:`2`,id:`canonical-message-format`,children:`Canonical message format`}),`\n`,_jsxs(_components.p,{children:[`The canonical message is a single string formed by concatenating these components `,_jsx(_components.strong,{children:`with no separator`}),`:`]}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`For GET requests:`})}),`\n`,_jsx(CodeBlock,{numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`[HTTP method][base URL][timestamp][user API key]`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`For POST, PUT, and other non-GET requests:`})}),`\n`,_jsx(CodeBlock,{numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`[HTTP method][base URL][content type][timestamp][user API key]`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:`The components map to your request as follows:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`HTTP method`}),` — uppercase, e.g. `,_jsx(_components.code,{children:`GET`}),`, `,_jsx(_components.code,{children:`POST`}),`, `,_jsx(_components.code,{children:`PUT`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Base URL`}),` — the scheme, host, and path only. `,_jsx(_components.strong,{children:`Do not include query parameters.`}),` For example, use `,_jsx(_components.code,{children:`https://api.portal.io/public/proposals`}),` even if the actual request URL has `,_jsx(_components.code,{children:`?PageNumber=1\u0026PageSize=10`}),` appended.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Content type`}),` — the exact value of the `,_jsx(_components.code,{children:`Content-Type`}),` header. Include this segment only for non-GET requests. The value differs per endpoint, so take it from that endpoint’s reference page rather than assuming one. Whatever you send, the content type in your signing string must match the `,_jsx(_components.code,{children:`Content-Type`}),` header exactly, or the request fails with `,_jsx(_components.code,{children:`401`}),`. For `,_jsx(_components.code,{children:`multipart/form-data`}),` the signing string must include the full `,_jsx(_components.code,{children:`boundary=\u003cvalue>`}),` parameter, not just `,_jsx(_components.code,{children:`multipart/form-data`}),` — see `,_jsx(_components.a,{href:`#sign-multipart-requests`,children:`Sign multipart requests`}),` below.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Timestamp`}),` — the exact value you send in `,_jsx(_components.code,{children:`X-MSS-CUSTOM-DATE`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`User API key`}),` — the exact value you send in `,_jsx(_components.code,{children:`X-MSS-API-USERKEY`})]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`rules`,children:`Rules`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Use the base URL `,_jsx(_components.strong,{children:`without`}),` query parameters. This is by design — query parameters are sent in the request as normal, but the server intentionally excludes them from signature verification. Only the scheme, host, and path are signed.`]}),`\n`,_jsxs(_components.li,{children:[`For `,_jsx(_components.code,{children:`GET`}),` requests, omit the content-type segment completely. Do not include an empty string in its place.`]}),`\n`,_jsxs(_components.li,{children:[`For non-`,_jsx(_components.code,{children:`GET`}),` requests, include the exact `,_jsx(_components.code,{children:`Content-Type`}),` value from the request header. The value in the signing string and the value in the header must match exactly — including case and any suffixes (e.g. `,_jsx(_components.code,{children:`application/json`}),`, not `,_jsx(_components.code,{children:`Application/JSON`}),`).`]}),`\n`,_jsxs(_components.li,{children:[`The request body is `,_jsx(_components.strong,{children:`not`}),` part of the canonical message. Only the content type is included, not the body itself.`]}),`\n`,_jsxs(_components.li,{children:[`The timestamp must exactly match the value in `,_jsx(_components.code,{children:`X-MSS-CUSTOM-DATE`}),`, character for character.`]}),`\n`,_jsxs(_components.li,{children:[`The user API key must exactly match the value in `,_jsx(_components.code,{children:`X-MSS-API-USERKEY`}),`, character for character.`]}),`\n`,_jsx(_components.li,{children:`For the initial credential exchange, the user API key is an empty string in both the header and the canonical message.`}),`\n`]}),`\n`,_jsx(Warning,{children:_jsx(_components.p,{children:`Do NOT Base64-decode the Secret Key before computing the HMAC. Use it exactly as provided — as raw ASCII bytes. Base64-decoding the key before use is a common mistake that produces an invalid signature.`})}),`\n`,_jsx(Heading,{level:`2`,id:`examples`,children:`Examples`}),`\n`,_jsxs(_components.p,{children:[`The timestamps in the canonical-message examples below are illustrative. For a real request, generate the current UTC timestamp and use that exact value in both the canonical message and the `,_jsx(_components.code,{children:`X-MSS-CUSTOM-DATE`}),` header, as shown in the full request example.`]}),`\n`,_jsx(Heading,{level:`3`,id:`get-request-credential-exchange`,children:`GET request (credential exchange)`}),`\n`,_jsx(_components.p,{children:`For the initial credential exchange, where the user API key is empty, the canonical message looks like this:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`GEThttps://api.portal.io/authenticate/apikeyexchangeMon, 06 Apr 2026 00:22:19 GMT`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:`Breaking that down:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Method: `,_jsx(_components.code,{children:`GET`})]}),`\n`,_jsxs(_components.li,{children:[`Base URL: `,_jsx(_components.code,{children:`https://api.portal.io/authenticate/apikeyexchange`})]}),`\n`,_jsxs(_components.li,{children:[`Content type: `,_jsx(_components.em,{children:`(omitted — this is a GET request)`})]}),`\n`,_jsxs(_components.li,{children:[`Timestamp: `,_jsx(_components.code,{children:`Mon, 06 Apr 2026 00:22:19 GMT`})]}),`\n`,_jsxs(_components.li,{children:[`User API key: `,_jsx(_components.em,{children:`(empty string — this is the initial exchange)`})]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Note that the actual HTTP request includes query parameters (`,_jsx(_components.code,{children:`?UserName=...\u0026Password=...`}),`), but the canonical message uses only the base URL without them.`]}),`\n`,_jsx(Heading,{level:`3`,id:`get-request-with-query-parameters`,children:`GET request (with query parameters)`}),`\n`,_jsx(_components.p,{children:`When listing proposals with pagination, the canonical message is:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`GEThttps://api.portal.io/public/proposalsMon, 06 Apr 2026 00:22:19 GMTqBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`The actual request URL includes `,_jsx(_components.code,{children:`?PageNumber=1\u0026PageSize=10`}),`, but those query parameters are not in the signed string.`]}),`\n`,_jsx(Heading,{level:`3`,id:`post-request-adding-an-area-to-a-proposal`,children:`POST request (adding an area to a proposal)`}),`\n`,_jsx(_components.p,{children:`For a POST request, the content type is included between the URL and the timestamp:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`POSThttps://api.portal.io/public/proposals/1042/areaapplication/jsonMon, 06 Apr 2026 00:22:19 GMTqBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:`Breaking that down:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Method: `,_jsx(_components.code,{children:`POST`})]}),`\n`,_jsxs(_components.li,{children:[`Base URL: `,_jsx(_components.code,{children:`https://api.portal.io/public/proposals/1042/area`})]}),`\n`,_jsxs(_components.li,{children:[`Content type: `,_jsx(_components.code,{children:`application/json`})]}),`\n`,_jsxs(_components.li,{children:[`Timestamp: `,_jsx(_components.code,{children:`Mon, 06 Apr 2026 00:22:19 GMT`})]}),`\n`,_jsxs(_components.li,{children:[`User API key: `,_jsx(_components.code,{children:`qBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=`})]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`The request body (`,_jsx(_components.code,{children:`{\"Name\": \"Living Room\"}`}),`) is sent normally but is `,_jsx(_components.strong,{children:`not`}),` part of the canonical message.`]}),`\n`,_jsx(Heading,{level:`3`,id:`sign-multipart-requests`,children:`Sign multipart requests`}),`\n`,_jsxs(_components.p,{children:[`For `,_jsx(_components.code,{children:`multipart/form-data`}),` requests, the content type segment of the canonical message must include the full `,_jsx(_components.code,{children:`boundary=\u003cvalue>`}),` parameter — the same value you send in the `,_jsx(_components.code,{children:`Content-Type`}),` header on the wire.`]}),`\n`,_jsxs(_components.p,{children:[`For an upload to `,_jsx(_components.code,{children:`POST /public/api/proposals/{ProposalId}/ai/content`}),`, the canonical message looks like this:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`POSThttps://api.portal.io/public/api/proposals/12345/ai/contentmultipart/form-data; boundary=PortalBoundary1748392012345Mon, 06 Apr 2026 00:22:19 GMTqBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:`Breaking that down:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Method: `,_jsx(_components.code,{children:`POST`})]}),`\n`,_jsxs(_components.li,{children:[`Base URL: `,_jsx(_components.code,{children:`https://api.portal.io/public/api/proposals/12345/ai/content`})]}),`\n`,_jsxs(_components.li,{children:[`Content type: `,_jsx(_components.code,{children:`multipart/form-data; boundary=PortalBoundary1748392012345`})]}),`\n`,_jsxs(_components.li,{children:[`Timestamp: `,_jsx(_components.code,{children:`Mon, 06 Apr 2026 00:22:19 GMT`})]}),`\n`,_jsxs(_components.li,{children:[`User API key: `,_jsx(_components.code,{children:`qBOSOYDeZaSzTxqMCL1Kr66JpU2H6wHCLz7xviZUOcA=`})]}),`\n`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Use the same boundary string in both the `,_jsx(_components.code,{children:`Content-Type`}),` header you send and the canonical message you sign. Most HTTP clients generate a random boundary internally and don’t expose it. To work around this, either pre-build the multipart body and `,_jsx(_components.code,{children:`Content-Type`}),` yourself before signing, or use a client like Python’s `,_jsx(_components.code,{children:`requests-toolbelt.MultipartEncoder`}),` that lets you fix the boundary at construction time. The full request body (form fields and file parts) is still `,_jsx(_components.strong,{children:`not`}),` included in the canonical message — only the content type with the boundary parameter is.`]})}),`\n`,_jsxs(_components.p,{children:[`If a multipart endpoint is called with a query string (for example `,_jsx(_components.code,{children:`?isMultiChunkUpload=true`}),`), the canonical message still uses the base URL `,_jsx(_components.strong,{children:`without`}),` the query string, following the same rule as every other Portal.io endpoint.`]}),`\n`,_jsx(Heading,{level:`2`,id:`computing-the-signature`,children:`Computing the signature`}),`\n`,_jsx(_components.p,{children:`Once you have the canonical message, compute the HMAC-SHA256 using your Secret Key as raw ASCII bytes, then Base64-encode the raw digest.`}),`\n`,_jsxs(CodeGroup,{children:[_jsx(CodeBlock,{filename:`python`,numberOfLines:`27`,language:`python`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`python`,children:_jsxs(_components.code,{language:`python`,numberOfLines:`27`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` hmac`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` hashlib`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` base64`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` urllib.parse `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` urlsplit, urlunsplit`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`def`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` sign_request`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`method`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`url`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`content_type`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`timestamp`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`user_key`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`secret_key`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`):`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Strip query string — only scheme + host + path are signed`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` parts `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` urlsplit(url)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` base_url `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` urlunsplit((parts.scheme, parts.netloc, parts.path, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`))`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Build canonical message`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` message_parts `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` [method.upper(), base_url]`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` if`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` method.upper() `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`!=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"GET\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` message_parts.append(content_type)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` message_parts.append(timestamp)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` message_parts.append(user_key)`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` canonical `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.join(message_parts)`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Compute HMAC-SHA256`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # Use secret_key as raw ASCII bytes — do NOT base64-decode it first`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` signature `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` hmac.new(`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` secret_key.encode(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"ascii\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` canonical.encode(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"utf-8\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` hashlib.sha256`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` ).digest()`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` return`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` base64.b64encode(signature).decode(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"ascii\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`)`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`node.js`,numberOfLines:`22`,language:`javascript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`javascript`,children:_jsxs(_components.code,{language:`javascript`,numberOfLines:`22`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` crypto`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` require`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'crypto'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`function`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` signRequest`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`method`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`url`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`contentType`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`timestamp`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`userKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#9CDCFE`},children:`secretKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`) {`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // Strip query string — only scheme + host + path are signed`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` baseUrl`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` new`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` URL`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`url`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` baseUrl`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`search`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` urlForSigning`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` baseUrl`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`toString`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`().`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`replace`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#D16969`},children:`/`}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-light-font-weight\":`bold`,\"--shiki-dark\":`#D7BA7D`,\"--shiki-dark-font-weight\":`inherit`},children:`\\\\/`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#DCDCAA`},children:`$`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#D16969`},children:`/`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // Build canonical message`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` let`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` parts`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` [`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`method`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`toUpperCase`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(), `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`urlForSigning`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`];`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` if`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` (`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`method`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`toUpperCase`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`() `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`!==`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'GET'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`) {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` parts`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`push`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`contentType`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` parts`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`push`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`timestamp`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`userKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` canonical`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` parts`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`join`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`''`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // Compute HMAC-SHA256`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` // Use secretKey as raw ASCII bytes — do NOT base64-decode it first`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` const`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4FC1FF`},children:` hmac`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` crypto`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`createHmac`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'sha256'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`Buffer`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`from`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`secretKey`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'ascii'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`));`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` hmac`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`update`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`canonical`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'utf8'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` return`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` hmac`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`digest`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'base64'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`);`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})})]}),`\n`,_jsx(Heading,{level:`2`,id:`full-request-example`,children:`Full request example`}),`\n`,_jsx(_components.p,{children:`Here is how the computed signature fits into a complete request. This example performs the initial credential exchange:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`13`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`13`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`url`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"https://sandbox.api.portal.io/authenticate/apikeyexchange?UserName=user%40example.com\u0026Password=MyP%40ss123\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`base_url`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"https://sandbox.api.portal.io/authenticate/apikeyexchange\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`timestamp`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"$(`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`date`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -u`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '+%a, %d %b %Y %H:%M:%S GMT')\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`canonical`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:'\"GET${'}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`base_url`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:\"}${\"}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`timestamp`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`}\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`signature`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"$(`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`printf`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` '%s' \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$canonical`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\" `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`|`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:` openssl`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` dgst `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`-sha256`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -hmac`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'YOUR_SECRET_KEY' `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`-binary`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` |`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:` base64`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`)\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`curl`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -i`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -X`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` GET`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$url`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"Accept: application/json\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-MSS-API-APPID: YOUR_APP_ID\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-MSS-API-USERKEY: \"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-MSS-CUSTOM-DATE: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$timestamp`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D7BA7D`},children:` \\\\`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -H`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"X-MSS-SIGNATURE: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`$signature`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`This example generates the current UTC timestamp in RFC 7231 format. The same value is used in the canonical message and the `,_jsx(_components.code,{children:`X-MSS-CUSTOM-DATE`}),` header.`]}),`\n`,_jsxs(_components.p,{children:[`Replace `,_jsx(_components.code,{children:`YOUR_APP_ID`}),` and `,_jsx(_components.code,{children:`YOUR_SECRET_KEY`}),` with your credentials. The `,_jsx(_components.code,{children:`X-MSS-API-USERKEY`}),` header is intentionally empty for this call.`]}),`\n`,_jsxs(_components.p,{children:[`Once you have your User API Key, include it in both `,_jsx(_components.code,{children:`X-MSS-API-USERKEY`}),` and your canonical message on all subsequent requests.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Sign Portal.io API Requests with HMAC-SHA256","description":"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.","href":"/authentication/signing-requests"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Canonical message format","slug":"canonical-message-format","depth":2,"children":[]},{"title":"Rules","slug":"rules","depth":2,"children":[]},{"title":"Examples","slug":"examples","depth":2,"children":[{"title":"GET request (credential exchange)","slug":"get-request-credential-exchange","depth":3,"children":[]},{"title":"GET request (with query parameters)","slug":"get-request-with-query-parameters","depth":3,"children":[]},{"title":"POST request (adding an area to a proposal)","slug":"post-request-adding-an-area-to-a-proposal","depth":3,"children":[]},{"title":"Sign multipart requests","slug":"sign-multipart-requests","depth":3,"children":[]}]},{"title":"Computing the signature","slug":"computing-the-signature","depth":2,"children":[]},{"title":"Full request example","slug":"full-request-example","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"portalio","siteName":"Portal.io API","title":"Sign Portal.io API Requests with HMAC-SHA256 - Portal.io API","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-18T15:55:25.138Z","originalFileLocation":"authentication/signing-requests.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Portal.io API\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Sign Portal.io API Requests with HMAC-SHA256 - Portal.io API\">\u003cmeta data-mint-head property=\"og:description\" content=\"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Portal.io API\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.portal.io/authentication/signing-requests\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://portalio.mintlify.app/_mintlify/api/og/v2?division=Authentication\u0026amp;title=Sign+Portal.io+API+Requests+with+HMAC-SHA256\u0026amp;description=Step-by-step+guide+to+building+the+canonical+message+and+computing+the+HMAC-SHA256+signature+required+on+every+Portal.io+API+request.\u0026amp;theme=17e1a5d314cfee72ac1850c3\u0026amp;signature=46ffd975540e5b3f8aa29c33991ccd5d3a84417cd85f4687ece5aa9abe162932\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Sign Portal.io API Requests with HMAC-SHA256 - Portal.io API\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://portalio.mintlify.app/_mintlify/api/og/v2?division=Authentication\u0026amp;title=Sign+Portal.io+API+Requests+with+HMAC-SHA256\u0026amp;description=Step-by-step+guide+to+building+the+canonical+message+and+computing+the+HMAC-SHA256+signature+required+on+every+Portal.io+API+request.\u0026amp;theme=17e1a5d314cfee72ac1850c3\u0026amp;signature=46ffd975540e5b3f8aa29c33991ccd5d3a84417cd85f4687ece5aa9abe162932\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/portalio/eSIwBZNUGF9qym-G/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Portal.io API\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#FF5000\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.portal.io/authentication/signing-requests\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/authentication/signing-requests.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.portal.io/#organization\",\"name\":\"Portal.io API\",\"url\":\"https://docs.portal.io\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/portalio/QuSRLNwghuEPmEf4/logo-files/portal-logo-light.svg?fit=max\u0026auto=format\u0026n=QuSRLNwghuEPmEf4\u0026q=85\u0026s=b46857228d074b069b086559f1b032ab\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.portal.io#website\",\"name\":\"Portal.io API\",\"url\":\"https://docs.portal.io\",\"publisher\":{\"@id\":\"https://docs.portal.io/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.portal.io/authentication/signing-requests#webpage\",\"url\":\"https://docs.portal.io/authentication/signing-requests\",\"name\":\"Sign Portal.io API Requests with HMAC-SHA256\",\"description\":\"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\",\"dateModified\":\"2026-09-18T15:55:25.138Z\",\"isPartOf\":{\"@id\":\"https://docs.portal.io#website\"},\"breadcrumb\":{\"@id\":\"https://docs.portal.io/authentication/signing-requests#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.portal.io/authentication/signing-requests#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Authentication\",\"item\":\"https://docs.portal.io/authentication/overview\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Sign Portal.io API Requests with HMAC-SHA256\",\"item\":\"https://docs.portal.io/authentication/signing-requests\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.portal.io/authentication/signing-requests#article\",\"headline\":\"Sign Portal.io API Requests with HMAC-SHA256\",\"name\":\"Sign Portal.io API Requests with HMAC-SHA256\",\"description\":\"Step-by-step guide to building the canonical message and computing the HMAC-SHA256 signature required on every Portal.io API request.\",\"url\":\"https://docs.portal.io/authentication/signing-requests\",\"mainEntityOfPage\":{\"@id\":\"https://docs.portal.io/authentication/signing-requests#webpage\"},\"dateModified\":\"2026-09-18T15:55:25.138Z\",\"publisher\":{\"@id\":\"https://docs.portal.io/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.portal.io#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Portal.io API\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Portal.io API","nav":{"global":null,"tabs":[{"tab":"Guide","icon":"book","groups":[{"group":"Get Started","pages":[{"title":"Get Started with the Portal.io API","href":"/quickstart"},{"title":"Use Portal.io in Your AI Tool","sidebarTitle":"AI Tools (MCP)","href":"/ai-tools"},{"title":"Explore with Postman","sidebarTitle":"Postman Collection","href":"/postman"},{"title":"Error Handling \u0026 Troubleshooting","sidebarTitle":"Troubleshooting","href":"/troubleshooting"},{"title":"Integration Field Matrix","sidebarTitle":"Field Matrix","href":"/field-matrix"}]},{"group":"Authentication","pages":[{"title":"Authenticate with the Portal.io API","description":"Portal.io API uses HMAC-SHA256 request signing. Learn how to get your API credentials, exchange them for a User Key, and include auth headers on every request.","href":"/authentication/overview"},{"title":"Sign Portal.io API Requests with HMAC-SHA256","href":"/authentication/signing-requests"}]},{"group":"Concepts","pages":[{"title":"Understanding Proposals in the Portal.io API","description":"Learn how Portal.io proposals are structured with areas, options, and change orders, and how to use the API to build and manage them.","href":"/concepts/proposals"},{"title":"Building a Proposal Item by Item","sidebarTitle":"Proposal Items Workflow","href":"/concepts/proposal-items-workflow"},{"title":"Reading Proposal Items","sidebarTitle":"Proposal Item Model","href":"/concepts/proposal-item-model"},{"title":"Managing Contacts and Locations in Portal.io","href":"/concepts/contacts"},{"title":"Understanding the Portal.io Catalog","href":"/concepts/catalog"},{"title":"Portal.io API Webhooks: Real-Time Event Notifications","href":"/concepts/webhooks"}]}]},{"tab":"API Reference","icon":"code","pages":[{"title":"API Reference","href":"/api-reference/authentication/exchange-api-key"}]},{"tab":"Updates","icon":"bolt","pages":[{"title":"Updates","href":"/changelog"}]}]},"title":"Sign Portal.io API Requests with HMAC-SHA256 - Portal.io API","buildId":"dpl_849ArpqvBiCczQiT1Y1stNGpbqHT","deploymentId":"dpl_849ArpqvBiCczQiT1Y1stNGpbqHT","dynamics":[],"chunks":[]}