Skip to content

Trust Center Uploads

This guide is the write path for your certification package. It shows how to upload a FedRAMP certification JSON artifact -- a Security Decision Record, Certification Overview, Vulnerability Detail, or Significant Change file -- into your system's Certification Documents surface using the REST API.

Where the Trust Center Access Guide covers reading your certification data, this guide covers putting a point-in-time artifact into it. The upload runs as three ordered API calls: two to the Halo Attachment endpoints and one to the storage URL the first call returns.

Related documentation


Before you start

You need an authenticated Halo API session. The Attachment endpoints require it, and Steps 1 and 3 carry the credential in the Authorization header.

Authentication is client-configurable. The worked examples below use the OAuth2 client_credentials bearer token described in the Access Guide's Get an access token step, which returns a token valid for one hour. Your provider may configure OAuth or another Halo-native mechanism; use whichever your credential package names. In the examples, YOUR_ACCESS_TOKEN is the token you obtained that way. The bearer-token mechanics are also described under Authentication.

You will also need four provider-issued values:

  • YOUR_BASE_URL -- the host of your Halo instance (the same host you call /auth/token on), used in Steps 1 and 3. This page spells the /api/ prefix explicitly, so YOUR_BASE_URL should not itself end in /api
  • YOUR_SITE_ID -- your system's site id
  • YOUR_FOLDER_ID -- the target folder inside Certification Documents
  • YOUR_DOCUMENT_TYPE -- the Certification Documents document-type id your provider assigns

Prepare the file

Before the first call, do two things:

  1. Have the file's exact byte size ready. Step 1 declares filesize in bytes, and that value must match the file you upload in Step 2. Send the file as raw bytes in Step 2; do not base64-encode it.
  2. Generate a fresh _tempid UUID. Create one UUID per file. You send it in Step 1 and again in Step 3, and it is what threads the three calls together into a single upload. Do not reuse a _tempid across files.

Flow overview

The upload is three ordered calls. Each depends on the one before it.

Step 1  Request a presigned URL    ->  returns upload URL + form fields
Step 2  Upload the file to storage ->  file lands in temporary storage
Step 3  Complete the upload        ->  file attached to Certification Documents

Step 1: Request a presigned URL

Ask the Attachment endpoint for a presigned upload URL. Send a JSON array with one object describing the file.

curl -s -X POST "https://YOUR_BASE_URL/api/Attachment/GetS3PresignedURL" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"_tempid":"YOUR_TEMPID","filename":"Security_Decision_Record_2026-09-03.json","filesize":111400}]'

YOUR_TEMPID is the fresh UUID you generated for this file. You send the same value again in Step 3.

Fields

Field Required Notes
_tempid yes The UUID you generated for this file. Reuse the same value in Step 3
filename yes The readable name of the file, for example Security_Decision_Record_2026-09-03.json
filesize yes Size of the file in bytes

The response returns three things: the presigned upload URL, a set of signed form fields you must echo back in Step 2, and the stored filename Halo assigned to the object (a hashed name such as 0123456789abcdef0123456789abcdef.json). Treat every field the response returns as required input to the next call, and keep the stored filename -- Step 3 requires it as filename.


Step 2: Upload the file to storage

POST the file to the presigned URL from the Step 1 response. The body is multipart/form-data and must contain every field the Step 1 response returned, plus one extra field, file, carrying the file's binary content.

# Send one -F per field the Step 1 response returned, echoing each unchanged,
# then one more -F for the file itself.
curl -s -X POST "https://PRESIGNED_URL_FROM_STEP_1" \
  -F "<echo each field name>=<its value from Step 1>" \
  -F "file=@Security_Decision_Record_2026-09-03.json"

Notes:

  • The presigned URL carries its own signed authorization in the fields Step 1 handed you, so this call goes to the storage endpoint, not to YOUR_BASE_URL, and does not take the Authorization: Bearer header.
  • Send back the Step 1 form fields exactly as received. Do not rename, reorder, or drop any of them.
  • The file field holds the file's raw bytes. Everything else in the body comes from Step 1.
  • Add the file part last. Storage ignores any form field that comes after it, so a signed field placed after file makes the upload fail with a policy error.

A successful Step 2 places the file in temporary storage. It is not yet attached to Certification Documents. Step 3 does that.


Step 3: Complete the upload

Tell the Attachment endpoint the storage upload is done and where the file belongs. Send a JSON array with one object.

curl -s -X POST "https://YOUR_BASE_URL/api/Attachment/PresignedURLUploadComplete" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d @complete.json
complete.json
[
  {
    "_tempid": "YOUR_TEMPID",
    "filename": "0123456789abcdef0123456789abcdef.json",
    "originalfilename": "Security_Decision_Record_2026-09-03.json",
    "filesize": 111400,
    "type": YOUR_DOCUMENT_TYPE,
    "unique_id": YOUR_SITE_ID,
    "atparentid": YOUR_FOLDER_ID
  }
]

type, unique_id, and atparentid are numeric ids. Send them unquoted, as shown; your provider gives you their values.

Fields

Field Required Notes
_tempid yes The same UUID you sent in Step 1
filename yes The stored name returned by Step 1, a hashed name such as 0123456789abcdef0123456789abcdef.json. This is not the readable name
originalfilename yes The readable name the file should keep in Certification Documents, for example Security_Decision_Record_2026-09-03.json
filesize yes Size of the file in bytes, the same value you sent in Step 1
type yes YOUR_DOCUMENT_TYPE, the Certification Documents document-type id your provider assigns. An integer, sent unquoted
unique_id yes YOUR_SITE_ID, your system's site id. An integer, sent unquoted
atparentid yes YOUR_FOLDER_ID, the target folder inside Certification Documents. An integer, sent unquoted

The three calls are ordered, and Step 3 is required

The calls run in sequence, threaded by the same _tempid. Step 2 uses the URL and fields from Step 1; Step 3 confirms what Step 2 uploaded. If you skip Step 3, the file lands in temporary storage but is never attached to Certification Documents, so it does not appear in the Trust Center. Run all three, in order, with one _tempid per file.


Quick reference

Step Operation Endpoint
1 Request a presigned URL POST {YOUR_BASE_URL}/api/Attachment/GetS3PresignedURL
2 Upload the file to storage POST to the presigned URL returned by Step 1
3 Complete the upload POST {YOUR_BASE_URL}/api/Attachment/PresignedURLUploadComplete

Governance

Uploading here is how the certification-sharing policy is met in practice. Two commitments in the Certification Data Sharing & Trust Center Policy and Procedures are met through this flow. P-14 requires a snapshot of the certification data aligned to each Ongoing Certification Report to be saved in Certification Documents; the FedRAMP-schema JSONs this guide uploads are those snapshots. P-15 requires FedRAMP Certification Reports to be posted within two weeks of receipt, without inappropriate modification, also as an upload to Certification Documents. This three-call flow is the mechanism for both, so post each artifact to the correct folder for your system, and for Certification Reports record the receipt date so the two-week window is evidenced.