curl --request POST \
--url https://www.ruddr.io/api/workspace/project-roles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"projectId": "<string>",
"isActive": true,
"isBillable": true,
"rate": 123,
"estimatedBillableHours": 123,
"estimatedNonBillableHours": 123,
"estimatedMonthlyBillableHours": 123,
"estimatedMonthlyNonBillableHours": 123,
"disciplineId": "<string>",
"practiceId": "<string>",
"locationId": "<string>"
}
'import requests
url = "https://www.ruddr.io/api/workspace/project-roles"
payload = {
"name": "<string>",
"projectId": "<string>",
"isActive": True,
"isBillable": True,
"rate": 123,
"estimatedBillableHours": 123,
"estimatedNonBillableHours": 123,
"estimatedMonthlyBillableHours": 123,
"estimatedMonthlyNonBillableHours": 123,
"disciplineId": "<string>",
"practiceId": "<string>",
"locationId": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
projectId: '<string>',
isActive: true,
isBillable: true,
rate: 123,
estimatedBillableHours: 123,
estimatedNonBillableHours: 123,
estimatedMonthlyBillableHours: 123,
estimatedMonthlyNonBillableHours: 123,
disciplineId: '<string>',
practiceId: '<string>',
locationId: '<string>'
})
};
fetch('https://www.ruddr.io/api/workspace/project-roles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://www.ruddr.io/api/workspace/project-roles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'projectId' => '<string>',
'isActive' => true,
'isBillable' => true,
'rate' => 123,
'estimatedBillableHours' => 123,
'estimatedNonBillableHours' => 123,
'estimatedMonthlyBillableHours' => 123,
'estimatedMonthlyNonBillableHours' => 123,
'disciplineId' => '<string>',
'practiceId' => '<string>',
'locationId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://www.ruddr.io/api/workspace/project-roles"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://www.ruddr.io/api/workspace/project-roles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.ruddr.io/api/workspace/project-roles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3e7a1b2c-8f4d-4a9e-b6c3-d2f5a8e1c9b7",
"name": "Senior Engineer",
"isActive": true,
"isBillable": true,
"rate": 175,
"createdAt": "2025-03-12T14:22:08.500Z",
"project": {
"id": "d8e3f7a2-c1b9-4d6e-8f2a-b5c7d3e9f1a6",
"name": "Platform Modernization",
"client": {
"id": "e1a4b8c3-d7f2-4e9a-b6c1-a3d8f4e7b2c9",
"name": "Acme Corporation"
}
},
"discipline": {
"id": "a91f4c2d-73b5-4e6a-8d1c-f3e7b2a9c4d6",
"name": "Engineering"
},
"practice": {
"id": "c45d8e3f-12a7-4b9c-9e2d-7f6a1b3c8d5e",
"name": "Software Development"
},
"location": {
"id": "f2b7c9a4-85d3-4f1e-a6b8-c3d9e7f2a1b4",
"name": "New York"
},
"budget": {
"billableHours": 320,
"nonBillableHours": 40
},
"monthlyBudget": {
"billableHours": 80,
"nonBillableHours": 10
},
"rateCards": [
{
"id": "f1a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8",
"start": null,
"end": "2025-06-30",
"rate": 175
},
{
"id": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
"start": "2025-07-01",
"end": null,
"rate": 175
}
]
}Create a project role
curl --request POST \
--url https://www.ruddr.io/api/workspace/project-roles \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"projectId": "<string>",
"isActive": true,
"isBillable": true,
"rate": 123,
"estimatedBillableHours": 123,
"estimatedNonBillableHours": 123,
"estimatedMonthlyBillableHours": 123,
"estimatedMonthlyNonBillableHours": 123,
"disciplineId": "<string>",
"practiceId": "<string>",
"locationId": "<string>"
}
'import requests
url = "https://www.ruddr.io/api/workspace/project-roles"
payload = {
"name": "<string>",
"projectId": "<string>",
"isActive": True,
"isBillable": True,
"rate": 123,
"estimatedBillableHours": 123,
"estimatedNonBillableHours": 123,
"estimatedMonthlyBillableHours": 123,
"estimatedMonthlyNonBillableHours": 123,
"disciplineId": "<string>",
"practiceId": "<string>",
"locationId": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
projectId: '<string>',
isActive: true,
isBillable: true,
rate: 123,
estimatedBillableHours: 123,
estimatedNonBillableHours: 123,
estimatedMonthlyBillableHours: 123,
estimatedMonthlyNonBillableHours: 123,
disciplineId: '<string>',
practiceId: '<string>',
locationId: '<string>'
})
};
fetch('https://www.ruddr.io/api/workspace/project-roles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://www.ruddr.io/api/workspace/project-roles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'projectId' => '<string>',
'isActive' => true,
'isBillable' => true,
'rate' => 123,
'estimatedBillableHours' => 123,
'estimatedNonBillableHours' => 123,
'estimatedMonthlyBillableHours' => 123,
'estimatedMonthlyNonBillableHours' => 123,
'disciplineId' => '<string>',
'practiceId' => '<string>',
'locationId' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://www.ruddr.io/api/workspace/project-roles"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://www.ruddr.io/api/workspace/project-roles")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.ruddr.io/api/workspace/project-roles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"projectId\": \"<string>\",\n \"isActive\": true,\n \"isBillable\": true,\n \"rate\": 123,\n \"estimatedBillableHours\": 123,\n \"estimatedNonBillableHours\": 123,\n \"estimatedMonthlyBillableHours\": 123,\n \"estimatedMonthlyNonBillableHours\": 123,\n \"disciplineId\": \"<string>\",\n \"practiceId\": \"<string>\",\n \"locationId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3e7a1b2c-8f4d-4a9e-b6c3-d2f5a8e1c9b7",
"name": "Senior Engineer",
"isActive": true,
"isBillable": true,
"rate": 175,
"createdAt": "2025-03-12T14:22:08.500Z",
"project": {
"id": "d8e3f7a2-c1b9-4d6e-8f2a-b5c7d3e9f1a6",
"name": "Platform Modernization",
"client": {
"id": "e1a4b8c3-d7f2-4e9a-b6c1-a3d8f4e7b2c9",
"name": "Acme Corporation"
}
},
"discipline": {
"id": "a91f4c2d-73b5-4e6a-8d1c-f3e7b2a9c4d6",
"name": "Engineering"
},
"practice": {
"id": "c45d8e3f-12a7-4b9c-9e2d-7f6a1b3c8d5e",
"name": "Software Development"
},
"location": {
"id": "f2b7c9a4-85d3-4f1e-a6b8-c3d9e7f2a1b4",
"name": "New York"
},
"budget": {
"billableHours": 320,
"nonBillableHours": 40
},
"monthlyBudget": {
"billableHours": 80,
"nonBillableHours": 10
},
"rateCards": [
{
"id": "f1a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8",
"start": null,
"end": "2025-06-30",
"rate": 175
},
{
"id": "a9b8c7d6-e5f4-4a3b-9c2d-1e0f9a8b7c6d",
"start": "2025-07-01",
"end": null,
"rate": 175
}
]
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
The name of the role (max 255 chars).
The project ID (uuid) for this role.
Whether the role is still active on the project.
Whether the role is billable. Defaults to the project's billability if not provided.
The billing rate for this role. Will only be applied for billable projects and roles. When Project Rate Cards is enabled, the new role gets this value on every rate card.
The number of billable hours to budget. This will only get used if useBudget is true and budgetMode is "detailed" on the project.
The number of non-billable hours to budget. This will only get used if useBudget is true and budgetMode is "detailed" on the project.
The number of billable hours on the monthly budget. This will only get used if useMonthlyBudget is true and monthlyBudgetMode is "detailed" on the project.
The number of non-billable hours on the monthly budget. This will only get used if useMonthlyBudget is true and monthlyBudgetMode is "detailed" on the project.
The discipline ID (uuid) for this role.
The practice ID (uuid) for this role.
The location ID (uuid) for this role.
Response
200
The unique identifier for the object.
The name of the role.
Whether the role is still active on the project.
Whether the role is billable. Overridden to false when the project is non-billable.
The billing rate for this role. null when the role or project is non-billable. When Project Rate Cards is enabled, this is the rate on the rate card covering today; see rateCards for other periods.
The timestamp when the object was created.
The project this role belongs to.
Show child attributes
Show child attributes
The discipline associated with this role. null if not assigned.
Show child attributes
Show child attributes
The practice associated with this role. null if not assigned.
Show child attributes
Show child attributes
The location associated with this role. null if not assigned.
Show child attributes
Show child attributes
The budget hours for this role. null when the project does not use a budget.
Show child attributes
Show child attributes
The monthly budget hours for this role. null when the project does not use a monthly budget in detailed mode.
Show child attributes
Show child attributes
The role's rates on each project rate card, earliest first. null if the Project Rate Cards feature is not enabled.
Show child attributes
Show child attributes