InvoicingValidate a contract hourly rate

Validate a contract hourly rate

Checks a contract-hourly-rate create body without writing anything.

A dry run of create-hourly-rate, but with DIFFERENT rules: it additionally refuses fixed-price-style contracts (38003) and unknown product numbers (37939, looked up in the account's default legal entity), and its "existing Standard rate" match requires the same name AND amount, so it does not warn that create would re-price a Standard rate with the same name. When an existing rate matches, no further rule is checked. Success is 200 with the literal body null.

Use it to:

  • Pre-flight a data import before calling create-hourly-rate

Not for:

  • Predicting create's outcome exactly

Rules across fields:

  • R1: ProjectSubContractId must be a contract of ProjectId (37042).
  • R2: If an active Standard rate with the same name and amount exists, or (category 1 / 3) a Project / Customer rate with the same name and amount, the call succeeds without checking the contract model or the category.

Behaviour:

  • Idempotent.
  • Safe to retry.
  • Changes are visible to the next read immediately.

Call first: GET /v1/contract

See also: GET /v1/contract-hourly-rate

Not yet documented: examples.

curl -X POST "https://app3.timelog.com/your-account/api/v1/contract-hourly-rate/validate-hourly-rate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
  "ProjectId": 123,
  "ProjectSubContractId": 123,
  "ContractHourlyRateName": "John Doe",
  "ContractHourlyRateValue": 3.14,
  "ProductNo": "example_string",
  "ContractHourlyRateCreateCategory": 42
}'
"null"
POST
/v1/contract-hourly-rate/validate-hourly-rate
POST
Base URLstring

Target server for requests. Edit to use your own host.

Bearer Token
Bearer Tokenstring
Required

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.
Content-Typestring
Required

The media type of the request body

Options: application/json
ProjectIdinteger

Project that owns the contract. Only used to check that the project exists and that ProjectSubContractId belongs to it. Can be set on create only. Refers to ProjectRead.ProjectID. Look up valid values with GET /v1/project/get-all. - Omitted, 0 or not an existing project — 37014 "No Project with ProjectID: {0} exist" — 500 Code 102 (captured for {}). Valid values come from GET /v1/project/get-all.

Format: int32
ProjectSubContractIdinteger

The contract (ProjectSubContract) that gets the rate. Can be set on create only. Refers to ContractRead.ContractID. Look up valid values with GET /v1/contract. - Not a contract of ProjectId (or does not exist) — 37042 — 500 Code 102. Valid values come from GET /v1/contract.

Format: int32
ContractHourlyRateNamestring

Name of the rate. Matched case-insensitively against the contract's existing rates. Can be set on create only. - Equal (case-insensitive) to the name of an active, non-indexed Standard rate on the contract — create: that rate is re-priced to ContractHourlyRateValue (indexation) instead of creating a rate. > Not yet documented: behaviour.

ContractHourlyRateValuenumber

Price per hour of the rate, in the currency of the project's price group (the project's currency list). Can be set on create only. Unit: project price-group currency per hour Contains financial data. - Omitted — 0 (non-nullable decimal); a 0 rate is created — no range check, negative values are not rejected either. - Category AddRateToStandard — Stored as the amount in the project's price group; every OTHER price group of the legal entity gets the new rate with amount 0. - Category AddRateToCustomer — The same number is stored in EVERY customer price group of the legal entity, whatever its currency.

Format: double
ProductNostring

Product number for invoice lines of this rate. Stored as sent (empty / whitespace → null on price-list rates). Can be set on create only. Refers to ProductNumberRead.ProductNumber. Look up valid values with GET /v1/product. - validate-hourly-rate, non-blank and not a product number of the account's DEFAULT legal entity — 37939 — 500 Code 102. Checked against the default legal entity, not the project's. - create-hourly-rate, any value — Not checked — an unknown product number is stored. Valid values come from GET /v1/product.

ContractHourlyRateCreateCategoryinteger

Where the new rate lives. Sent as an integer. Can be set on create only. - Omitted / 0 / any other value, on validate — 300132 "User does not have privileges to save hourly rate." — 500 Code 102; the message is misleading. Allowed values: | Value | Name | Meaning | |---|---|---| | 1 | AddRateToContract | Project-specific rate: created on this contract only (Type Project), in the project's price group. | | 2 | AddRateToStandard | New hourly-rate service in the project legal entity's STANDARD price list (a rate in every price group of the legal entity: the amount in the project's group, 0 elsewhere), copied to the contract as a Standard rate. | | 3 | AddRateToCustomer | New customer-specific service for the project's customer (customer price group created if missing; the same amount in every customer price group), copied to the contract as a Customer rate. | > Not yet documented: values → serialization.

Format: int32 • Options: 1, 2, 3
Request Preview
Response

Response will appear here after sending the request

Authentication

header
Authorizationstring
Required

Bearer token. OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).

The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.

Not yet documented: how to obtain a token.

Not yet documented: token lifetime.

Body

application/json
ProjectIdinteger

Project that owns the contract. Only used to check that the project exists and that ProjectSubContractId belongs to it.

Can be set on create only.

Refers to ProjectRead.ProjectID. Look up valid values with GET /v1/project/get-all.

  • Omitted, 0 or not an existing project — 37014 "No Project with ProjectID: {0} exist" — 500 Code 102 (captured for &#123;}).

Valid values come from GET /v1/project/get-all.

ProjectSubContractIdinteger

The contract (ProjectSubContract) that gets the rate.

Can be set on create only.

Refers to ContractRead.ContractID. Look up valid values with GET /v1/contract.

  • Not a contract of ProjectId (or does not exist) — 37042 — 500 Code 102.

Valid values come from GET /v1/contract.

ContractHourlyRateNamestring

Name of the rate. Matched case-insensitively against the contract's existing rates.

Can be set on create only.

  • Equal (case-insensitive) to the name of an active, non-indexed Standard rate on the contract — create: that rate is re-priced to ContractHourlyRateValue (indexation) instead of creating a rate.

Not yet documented: behaviour.

ContractHourlyRateValuenumber

Price per hour of the rate, in the currency of the project's price group (the project's currency list).

Can be set on create only.

Unit: project price-group currency per hour

Contains financial data.

  • Omitted — 0 (non-nullable decimal); a 0 rate is created — no range check, negative values are not rejected either.

  • Category AddRateToStandard — Stored as the amount in the project's price group; every OTHER price group of the legal entity gets the new rate with amount 0.

  • Category AddRateToCustomer — The same number is stored in EVERY customer price group of the legal entity, whatever its currency.

ProductNostring

Product number for invoice lines of this rate. Stored as sent (empty / whitespace → null on price-list rates).

Can be set on create only.

Refers to ProductNumberRead.ProductNumber. Look up valid values with GET /v1/product.

  • validate-hourly-rate, non-blank and not a product number of the account's DEFAULT legal entity — 37939 — 500 Code 102. Checked against the default legal entity, not the project's.

  • create-hourly-rate, any value — Not checked — an unknown product number is stored.

Valid values come from GET /v1/product.

ContractHourlyRateCreateCategoryinteger

Where the new rate lives. Sent as an integer.

Can be set on create only.

  • Omitted / 0 / any other value, on validate — 300132 "User does not have privileges to save hourly rate." — 500 Code 102; the message is misleading.

Allowed values:

ValueNameMeaning
1AddRateToContractProject-specific rate: created on this contract only (Type Project), in the project's price group.
2AddRateToStandardNew hourly-rate service in the project legal entity's STANDARD price list (a rate in every price group of the legal entity: the amount in the project's group, 0 elsewhere), copied to the contract as a Standard rate.
3AddRateToCustomerNew customer-specific service for the project's customer (customer price group created if missing; the same amount in every customer price group), copied to the contract as a Customer rate.

Not yet documented: values → serialization.

Allowed values:123

Responses

*Success without content: HTTP 200 with the literal JSON body null. Literal JSON null (TafSuccessResult).

Order: none

Empty result: none*