Skip to content
Rigel docs
Open Rigel

Correct a quote or an invoice

maintenance.invoice.update

POST/api/v1/households/{household_id}/commands/maintenance.invoice.update

  • Command
  • Rigel runs it
  • Applies offline

Sets only the fields given; null clears an optional one. When lines is given it replaces all the old lines, so send the whole list. Everything linked must be at the invoice's property (different_property).

Parameters#

Correct a quote or an invoice: parameters
FieldDescription
household_idpathstringrequired

The household's ID.

Idempotency-Keyheaderstringoptional

The command's ID. Reuse it when retrying the same command.

Request body#

JSON, as application/json.

Correct a quote or an invoice: request body fields
FieldDescription
invoice_idstringrequired

A invoice ID, such as ivc_01JC0000000000000000000000.

kindone of: quote, invoiceoptional

quote (an estimate of work) or invoice (a bill for work done).

file_idstring or nulloptional

The original paper; null detaches it.

contractor_idstring or nulloptional

Who issued it; null clears it.

visit_idstring or nulloptional

The visit it is for; null clears it.

project_idstring or nulloptional

The project it is for; null clears it.

numberstring or nulloptionalNo description.at most 100 characters
issued_onstring (date) or nulloptional

A calendar date, YYYY-MM-DD.

service_onstring (date) or nulloptional

A calendar date, YYYY-MM-DD.

currencystringoptional

An ISO 4217 currency code, such as USD.

pattern ^[A-Z]{3}$
subtotalinteger or nulloptional

The amount before tax, in minor units.

-1,000,000,000 to 1,000,000,000
taxinteger or nulloptional

The tax, in minor units.

-1,000,000,000 to 1,000,000,000
totalinteger or nulloptional

The total as the paper gives it, in minor units. Recording it doesn't mean it was paid.

-1,000,000,000 to 1,000,000,000
notesstring or nulloptional

Anything else worth keeping.

at most 20,000 characters
linesarray of objectsoptional

The paper's line items, in order. Replaces the existing lines.

at most 200 items
lines[].descriptionstringrequired

What the line is for, as the paper words it.

at most 500 characters
lines[].kindone of: labor, part, fee, tax, discount, otheroptional

labor, part, fee, tax, discount or other. Defaults to other.

lines[].quantitynumberoptional

How many, or how many hours.

at least 0
lines[].unit_priceintegeroptional

The price of one, in minor units.

-1,000,000,000 to 1,000,000,000
lines[].amountintegeroptional

The line's total in minor units; negative for a discount.

-1,000,000,000 to 1,000,000,000
lines[].asset_idstringoptional

The item this line is for.

Response#

200 The command was applied.

Correct a quote or an invoice: response fields
FieldDescription
resultobjectrequired

What the command returned, such as the IDs it created.

versionintegerrequired

The household version after the command.

Errors#

Every error has the same envelope: error.code, error.message, error.details, error.recovery and error.request_id.

Correct a quote or an invoice: errors
StatusCodeWhen
400validation_failedA field is missing, misspelled or out of range. details.fields lists each problem.
401unauthenticatedThe request has no valid session.
404file_not_foundA file that isn't there.
404invoice_not_foundAn invoice that isn't there.
404not_foundThe household doesn't exist, or you aren't a member of it.
409different_property
  • A visit at another property.
  • A line's item at another property.
409idempotency_key_reusedThe Idempotency-Key was already used for a different command.

Examples#

These are real. Each scenario is one of the command's test cases, run on a household when this site was built, and the response is what the API returned.

Changes only what's given and leaves the lines

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "total": 200,
  "number": null,
  "kind": "quote"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    total: 200,
    number: null,
    kind: "quote",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "total": 200,
  "number": null,
  "kind": "quote"
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R07VNMM80NJWV1GA8W

{
  "result": {},
  "version": 1
}

Lines given replace the old ones

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": [
    {
      "description": "Everything",
      "amount": 500,
      "asset_id": "ast_01JC00000000000000000000A1"
    },
    {
      "description": "Discount",
      "kind": "discount",
      "amount": -50
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    lines: [
      {
        description: "Everything",
        amount: 500,
        asset_id: "ast_01JC00000000000000000000A1",
      },
      {
        description: "Discount",
        kind: "discount",
        amount: -50,
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": [
    {
      "description": "Everything",
      "amount": 500,
      "asset_id": "ast_01JC00000000000000000000A1"
    },
    {
      "description": "Discount",
      "kind": "discount",
      "amount": -50
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0SNH937RK01ME08M0

{
  "result": {},
  "version": 1
}

An empty list removes every line

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    lines: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0X2Z0M7KY7ETGNC90

{
  "result": {},
  "version": 1
}

Links can be set and cleared

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "contractor_id": "ctr_01JC00000000000000000000K1",
  "visit_id": "vst_01JC00000000000000000000V1",
  "project_id": null,
  "file_id": null
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    contractor_id: "ctr_01JC00000000000000000000K1",
    visit_id: "vst_01JC00000000000000000000V1",
    project_id: null,
    file_id: null,
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "contractor_id": "ctr_01JC00000000000000000000K1",
  "visit_id": "vst_01JC00000000000000000000V1",
  "project_id": null,
  "file_id": null
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R012D09AWS3WCFARBD

{
  "result": {},
  "version": 1
}

A visit at another property

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "visit_id": "vst_01JC00000000000000000000V1"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    visit_id: "vst_01JC00000000000000000000V1",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "visit_id": "vst_01JC00000000000000000000V1"
}
Response, 409
HTTP/1.1 409 Conflict
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0N4ADM96K3EMT4KYV

{
  "error": {
    "code": "different_property",
    "message": "That visit is at a different property. No changes were applied.",
    "details": {
      "property_id": "prp_01JC00000000000000000000P2"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0N4ADM96K3EMT4KYV"
  }
}

A line's item at another property

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": [
    {
      "description": "x",
      "asset_id": "ast_01JC00000000000000000000A2"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    lines: [
      {
        description: "x",
        asset_id: "ast_01JC00000000000000000000A2",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "lines": [
    {
      "description": "x",
      "asset_id": "ast_01JC00000000000000000000A2"
    }
  ]
}
Response, 409
HTTP/1.1 409 Conflict
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0DSQJAV47RESBGP7M

{
  "error": {
    "code": "different_property",
    "message": "That item is at a different property. No changes were applied.",
    "details": {
      "property_id": "prp_01JC00000000000000000000P2"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0DSQJAV47RESBGP7M"
  }
}

A file that isn't there

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "file_id": "fil_01JC00000000000000000000F1"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    file_id: "fil_01JC00000000000000000000F1",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "file_id": "fil_01JC00000000000000000000F1"
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R061X6AJMTSKYF5QQ5

{
  "error": {
    "code": "file_not_found",
    "message": "That file isn't there any more. No changes were applied.",
    "details": {
      "file_id": "fil_01JC00000000000000000000F1"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R061X6AJMTSKYF5QQ5"
  }
}

An invoice that isn't there

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "total": 1
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    invoice_id: "ivc_01JC00000000000000000000B1",
    total: 1,
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/maintenance.invoice.update HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "invoice_id": "ivc_01JC00000000000000000000B1",
  "total": 1
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0SPJJ38T35RCSQTD0

{
  "error": {
    "code": "invoice_not_found",
    "message": "That quote or invoice isn't there any more. No changes were applied.",
    "details": {
      "invoice_id": "ivc_01JC00000000000000000000B1"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0SPJJ38T35RCSQTD0"
  }
}

For Rigel#

Rigel's tool for this command is maintenance_invoice_update. Rigel runs this on its own, then says what it did. The agent guide has the rest.

Rigel is in early access, and sign-up is by invitation. Open Rigel