Skip to main content
BackedHelp Centre

Write API: milestones, payments, contracts and files

Create and approve milestones, mark payment batches paid, record contract statuses, and upload and attach files from another system through the Backed API.

Last updated

This page is for your organisation's developers. It lists the Backed API routes that create or change milestones, payments and contracts, and that upload files, and what each one refuses. For registrations, applications, notes, tags and assessor assignments, see Write API: registrations, applications, notes, tags and assignments.

No write route reads or changes a bank account number. A bank_account_number in a request body is refused with 422 unknown_field, and no write response carries one.

Before you start

A credential can only read until you give it a write scope and an Administrator to act as:

  1. Go to Settings > Profile and terms and open the API Access tab.
  2. Select Edit scopes on the credential's row, or New Credential for a new one.
  3. Tick the scopes the system needs. See API access.
  4. Choose the Administrator it acts as under Acts as. This needs Can edit permissions.
  5. Select Save.

The six scopes

Scope Label on the tab Opens
read Read Every read route. Every credential has it.
write:registrations Write registrations The registration routes, on the part one page.
write:applications Write applications The transition, note and tag routes, on the part one page.
write:assessments Write assessments The assignment route, on the part one page.
write:post_award Write post-award records The milestone, payment and contract routes on this page.
write:files Write files The file routes on this page.

Who a write runs as

Every write runs with the permissions, data access rules and approval limit of the credential's Acts as Administrator, so it can't do anything they couldn't do in Backed. It goes through the same checks, sends the same messages and records the same audit entries as the action on the screen.

In the record's history a write shows the credential's name, then Acting as and the Administrator's name.

If that Administrator is removed, disabled or no longer an Administrator, every write answers 403 run_as_disabled until you choose someone else. Reads carry on.

The Idempotency-Key header

Send your access token as a bearer token and this header on every POST: 1 to 255 characters your system makes up, unique to that change.

  • The same key and the same body within 24 hours answers the first response again, with the header Idempotent-Replayed: true, and changes nothing.
  • The same key with a different body or path is refused with 422 idempotency_key_reused.
  • No key is refused with 400 idempotency_key_required.
  • A refused request stores nothing, so you can retry it with the same key.

No route on this page is a PATCH, so none takes If-Match. That header belongs to the PATCH route on the part one page, where an out-of-date lock_version is refused with 412 precondition_failed.

Routes

Every path is on your organisation's host. Each route can also give the refusals in Refusals every write can give.

Method Path Scope Answers Refusals of its own
POST /api/v2/milestones write:post_award 201 404 module_disabled
POST /api/v2/milestones/:id/approve write:post_award 200 404 module_disabled, 404 not_found, 403 approval_limit_exceeded, 409 invalid_transition, 409 invalid_state
POST /api/v2/payments/:id/mark_paid write:post_award 200 404 module_disabled, 404 not_found, 409 invalid_transition
POST /api/v2/contracts/:id/status write:post_award 200 404 module_disabled, 404 not_found, 409 invalid_transition
POST /api/v2/files write:files 201 404 not_found, 409 invalid_state, 413 payload_too_large, 415 unsupported_media_type
POST /api/v2/files/:id/confirm write:files 200 404 not_found, 409 invalid_state
POST /api/v2/files/:id/attach write:files 200 404 not_found, 409 invalid_state, 409 file_not_scanned, 422 file_infected

404 module_disabled means the module is turned off for your organisation: Contracts for the milestone and contract routes, and Payments for the payment route. The file routes have no module of their own.

Another organisation's id answers the same 404 not_found as an id that was never issued.

Milestones

POST /api/v2/milestones creates one milestone on an application, as Create Milestone does. Send funding_application_id, title, due_date (YYYY-MM-DD), amount (such as "48500.00") and form_template_id, which is the deliverables form. You can also send milestone_template_id, description and reviewer_user_ids. The ids in this body are strings of digits. An application or form template that doesn't exist or that the Administrator can't see is refused with 422 validation_failed on that field.

POST /api/v2/milestones/:id/approve does what Approve Milestone does and takes no body. Both answer with the milestone as GET /api/v2/milestones/:id reads it.

  • An amount above the Administrator's Milestone Approval Limit is refused with 403 approval_limit_exceeded.
  • A milestone that can't be approved from where it stands is refused with 409 invalid_transition, and current_status says where it stands.
  • A deliverable not yet approved, or a change made while it was being approved, is refused with 409 invalid_state. The text says which.

Payments

POST /api/v2/payments/:id/mark_paid does what Mark as Paid does, so :id is a payment batch, not a single payment. Send paid_date (YYYY-MM-DD) and reference, both required. paid_date goes on each payment marked, and reference on the batch's paid entry in its trail.

A batch that isn't approved, or that is already paid, is refused with 409 invalid_transition and nothing changes. It answers id, status, marked_paid_at, paid_date, reference, payment_count and amount_total.

Contracts

POST /api/v2/contracts/:id/status takes a status and answers with the contract as GET /api/v2/contracts/:id reads it.

  • executed is the Administrator's own counter-signature, by typed name. Backed has no offline signing method, so the Administrator must be a signatory of this contract and it must be their turn to sign. It executes the contract when theirs is the last signature.
  • terminated needs a reason. The contract stays terminating while the termination letter is sent in Backed, as it does on the screen.
  • Any other status, completed included, is refused with 409 invalid_transition. So is a move that isn't open from where the contract stands, and current_status says where that is.

Files

Uploading takes three steps. Send an Idempotency-Key on each, with no body on the last two.

  1. Reserve. POST /api/v2/files takes filename, content_type, size in bytes and target. It answers an id for the next two steps, the upload to make, expires_at, max_bytes and state. The upload is valid for 15 minutes.
  2. Upload, then confirm. If upload.method is POST, send a multipart form to upload.url with upload.fields in order and the file last, in a part named file. If it is PUT, send the bytes with upload.headers. Then POST /api/v2/files/:id/confirm starts the malware scan and answers state: scanning. Confirming before the upload is stored is refused with 409 invalid_state.
  3. Attach. POST /api/v2/files/:id/attach puts the clean file on its target and answers id, state: attached and target. While the scan is running it is refused with 409 file_not_scanned, so wait and retry. A file that failed the scan is refused with 422 file_infected and is never attached. A file never confirmed answers 404 not_found. Attaching again answers the same and adds nothing.

Only the credential that reserved an upload can confirm or attach it. For anyone else the id answers 404 not_found.

target holds a type, the record's id, and for a deliverable the field:

target.type target.id Also needs The Administrator needs
milestone_deliverable The milestone field: the form's File upload field The permission to complete on behalf
registration_entity The registration Nothing The permission to edit applicants
application_note The application note Nothing Always answers 404 not_found

Only the applicant or verifier side of an application can attach to its notes, so a request naming an application_note answers 404 not_found for an Administrator, as it does on the screen.

A file over 100 MB or over its target's own limit is refused with 413 payload_too_large, and a type its target doesn't accept with 415 unsupported_media_type, both before anything is stored. A target that is full or closed to files is refused with 409 invalid_state.

Refusals every write can give

A refusal's body is {"error": "<code>", "error_description": "<text>"}. Your system should switch on error.

Status error Why
400 invalid_request The body isn't a JSON object.
400 idempotency_key_required There's no Idempotency-Key, or it's longer than 255 characters.
403 insufficient_scope The credential doesn't hold the route's scope. The text names the scope.
403 run_as_disabled The credential has no enabled Administrator to act as.
403 forbidden The Administrator's permissions or data access rules don't allow this on this record.
413 payload_too_large The request body is too large.
415 unsupported_media_type The request body isn't application/json.
422 idempotency_key_reused The key was already used for a different request.
422 unknown_field The body has a field the route doesn't document. The text names the first one.
422 validation_failed A value breaks the screen's own rules. fields names each failing field.

See also

This page uses Backed's default terms, such as Funding Round and Application Template. An organisation may rename them, so read each as your organisation's own term if you've renamed it.