# Create an additional appointment Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/create-an-additional-appointment /openapi.json post /api/v1/additional_appointments Create a new additional appointment # Delete an additional appointment Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/delete-an-additional-appointment /openapi.json delete /api/v1/additional_appointments/{id} Delete a additional appointment # Get an additional appointment Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/get-an-additional-appointment /openapi.json get /api/v1/additional_appointments/{id} Returns a specific additional appointment including configured record extras # List additional appointments Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/list-additional-appointments /openapi.json get /api/v1/additional_appointments Returns a list of additional appointments # List additional appointments for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/list-additional-appointments-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/additional_appointments # Update an additional appointment Source: https://api-docs.scholarlysoftware.com/api-reference/additional-appointments/update-an-additional-appointment /openapi.json patch /api/v1/additional_appointments/{id} Update a additional appointment # Create an address for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/create-an-address-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/addresses # Create an address for an user Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/create-an-address-for-an-user /openapi.json post /api/v1/users/{user_id}/addresses # Delete an address Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/delete-an-address /openapi.json delete /api/v1/addresses/{id} Deletes a specific address ### An important note on deleting addresses This resource is effective-dated in the Scholarly data model, which means you should almost-never delete an address. Instead, you should set the `effective_to` property via updating an address to signify that the address is no longer in use. # Get an address Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/get-an-address /openapi.json get /api/v1/addresses/{id} Returns a specific address by UUID # List addresses Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/list-addresses /openapi.json get /api/v1/addresses Returns a list of addresses # List addresses for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/list-addresses-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/addresses Returns a list of addresses # List addresses for an user Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/list-addresses-for-an-user /openapi.json get /api/v1/users/{user_id}/addresses Returns a list of addresses # Update an address Source: https://api-docs.scholarlysoftware.com/api-reference/addresses/update-an-address /openapi.json patch /api/v1/addresses/{id} Updates a specific address # Create an admin assistant role permission Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-role-permissions/create-an-admin-assistant-role-permission /openapi.json post /api/v1/admin_assistant_role_permissions Grant a permission to an admin assistant role # Delete an admin assistant role permission Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-role-permissions/delete-an-admin-assistant-role-permission /openapi.json delete /api/v1/admin_assistant_role_permissions/{id} Revoke a permission from an admin assistant role # Get an admin assistant role permission Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-role-permissions/get-an-admin-assistant-role-permission /openapi.json get /api/v1/admin_assistant_role_permissions/{id} Returns a specific admin assistant role permission # List admin assistant role permissions Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-role-permissions/list-admin-assistant-role-permissions /openapi.json get /api/v1/admin_assistant_role_permissions Returns a list of admin assistant role permissions # Create an admin assistant role Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/create-an-admin-assistant-role /openapi.json post /api/v1/admin_assistant_roles Create a new admin assistant role mapping # Delete an admin assistant role Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/delete-an-admin-assistant-role /openapi.json delete /api/v1/admin_assistant_roles/{id} Delete an admin assistant role mapping # Get an admin assistant role Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/get-an-admin-assistant-role /openapi.json get /api/v1/admin_assistant_roles/{id} Returns a specific admin assistant role mapping # List admin assistant roles Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/list-admin-assistant-roles /openapi.json get /api/v1/admin_assistant_roles Returns a list of admin assistant role mappings # List admin assistant roles for an user Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/list-admin-assistant-roles-for-an-user /openapi.json get /api/v1/users/{user_id}/admin_assistant_roles Returns a list of admin assistant role mappings # Update an admin assistant role Source: https://api-docs.scholarlysoftware.com/api-reference/admin-assistant-roles/update-an-admin-assistant-role /openapi.json patch /api/v1/admin_assistant_roles/{id} Update an admin assistant role mapping # Create a backup Source: https://api-docs.scholarlysoftware.com/api-reference/backups/create-a-backup /openapi.json post /api/v1/backups Creates a new database backup and queues the export job # Get a backup Source: https://api-docs.scholarlysoftware.com/api-reference/backups/get-a-backup /openapi.json get /api/v1/backups/{id} Returns details of a specific backup # List backups Source: https://api-docs.scholarlysoftware.com/api-reference/backups/list-backups /openapi.json get /api/v1/backups Returns a list of database backups # Create a campus Source: https://api-docs.scholarlysoftware.com/api-reference/campuses/create-a-campus /openapi.json post /api/v1/campuses # Delete a campus Source: https://api-docs.scholarlysoftware.com/api-reference/campuses/delete-a-campus /openapi.json delete /api/v1/campuses/{id} # Get a campus Source: https://api-docs.scholarlysoftware.com/api-reference/campuses/get-a-campus /openapi.json get /api/v1/campuses/{id} # List campuses Source: https://api-docs.scholarlysoftware.com/api-reference/campuses/list-campuses /openapi.json get /api/v1/campuses List all campuses # Update a campus Source: https://api-docs.scholarlysoftware.com/api-reference/campuses/update-a-campus /openapi.json patch /api/v1/campuses/{id} # Create a credit line item for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/create-a-credit-line-item-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/credit_line_items # Create a line item for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/create-a-line-item-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/credits/line_items # Delete a credit line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/delete-a-credit-line-item /openapi.json delete /api/v1/credit_line_items/{id} # Delete a line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/delete-a-line-item /openapi.json delete /api/v1/credits/line_items/{id} Deletes a specific credit line item # Get a credit line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/get-a-credit-line-item /openapi.json get /api/v1/credit_line_items/{id} # Get a line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/get-a-line-item /openapi.json get /api/v1/credits/line_items/{id} Returns a specific credit line item by UUID # List credit line items Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/list-credit-line-items /openapi.json get /api/v1/credit_line_items # List credit line items for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/list-credit-line-items-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/credit_line_items # List line items Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/list-line-items /openapi.json get /api/v1/credits/line_items Returns a list of credit line items # List line items for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/list-line-items-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/credits/line_items Returns a list of credit line items # Update a credit line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/update-a-credit-line-item /openapi.json patch /api/v1/credit_line_items/{id} # Update a line item Source: https://api-docs.scholarlysoftware.com/api-reference/credit-line-items/update-a-line-item /openapi.json patch /api/v1/credits/line_items/{id} Updates a specific credit line item # Create a department chair Source: https://api-docs.scholarlysoftware.com/api-reference/department-chairs/create-a-department-chair /openapi.json post /api/v1/department_chairs Create a new department chair assignment # Delete a department chair Source: https://api-docs.scholarlysoftware.com/api-reference/department-chairs/delete-a-department-chair /openapi.json delete /api/v1/department_chairs/{id} Delete a department chair assignment # Get a department chair Source: https://api-docs.scholarlysoftware.com/api-reference/department-chairs/get-a-department-chair /openapi.json get /api/v1/department_chairs/{id} Returns a specific department chair assignment # List department chairs Source: https://api-docs.scholarlysoftware.com/api-reference/department-chairs/list-department-chairs /openapi.json get /api/v1/department_chairs Returns a list of department chair assignments # Create a department Source: https://api-docs.scholarlysoftware.com/api-reference/departments/create-a-department /openapi.json post /api/v1/departments Creates a new department following JSON:API specification # Delete a department Source: https://api-docs.scholarlysoftware.com/api-reference/departments/delete-a-department /openapi.json delete /api/v1/departments/{id} Deletes a department following JSON:API specification # Get a department Source: https://api-docs.scholarlysoftware.com/api-reference/departments/get-a-department /openapi.json get /api/v1/departments/{id} Returns details of a specific department # Get parent department Source: https://api-docs.scholarlysoftware.com/api-reference/departments/get-parent-department /openapi.json get /api/v1/departments/{id}/parent # Get parent relationship linkage Source: https://api-docs.scholarlysoftware.com/api-reference/departments/get-parent-relationship-linkage /openapi.json get /api/v1/departments/{id}/relationships/parent Returns JSON:API relationship linkage data for the parent department. # List child departments Source: https://api-docs.scholarlysoftware.com/api-reference/departments/list-child-departments /openapi.json get /api/v1/departments/{id}/children # List children relationship linkage Source: https://api-docs.scholarlysoftware.com/api-reference/departments/list-children-relationship-linkage /openapi.json get /api/v1/departments/{id}/relationships/children Returns JSON:API relationship linkage data for child departments. # List departments Source: https://api-docs.scholarlysoftware.com/api-reference/departments/list-departments /openapi.json get /api/v1/departments ### A Note on Department Structure A "department" in Scholarly can be much more than just a department. It can be a college, department, campus location, sub-department, or unit. Departments are organized in a tree structure, with each department optionally having a parent and 0 or more children. Departments without a parent are labeled as "root" departments. By default, this endpoint returns only root departments. Pass `filter[flat]=true` to return a flat list of all departments (including children), which is useful for quick lookups by name or external ID. # Update a department Source: https://api-docs.scholarlysoftware.com/api-reference/departments/update-a-department /openapi.json patch /api/v1/departments/{id} Updates a department's name following JSON:API specification # Update parent relationship Source: https://api-docs.scholarlysoftware.com/api-reference/departments/update-parent-relationship /openapi.json patch /api/v1/departments/{id}/relationships/parent Sets or removes the parent department. Pass null data to make this a root department. # Create an employment for an user Source: https://api-docs.scholarlysoftware.com/api-reference/employments/create-an-employment-for-an-user /openapi.json post /api/v1/users/{user_id}/employments # Delete an employment Source: https://api-docs.scholarlysoftware.com/api-reference/employments/delete-an-employment /openapi.json delete /api/v1/employments/{id} Deletes a specific employment ### An important note on deleting employments This resource is effective-dated in the Scholarly data model, which means you should almost-never delete an employment. Instead, you should set the `effective_to` property via updating an employment to signify that the employment has ended. # Get an employment Source: https://api-docs.scholarlysoftware.com/api-reference/employments/get-an-employment /openapi.json get /api/v1/employments/{id} Returns a specific employment by UUID # List employments Source: https://api-docs.scholarlysoftware.com/api-reference/employments/list-employments /openapi.json get /api/v1/employments Returns a list of employments # List employments for an user Source: https://api-docs.scholarlysoftware.com/api-reference/employments/list-employments-for-an-user /openapi.json get /api/v1/users/{user_id}/employments Returns a list of employments # Update an employment Source: https://api-docs.scholarlysoftware.com/api-reference/employments/update-an-employment /openapi.json patch /api/v1/employments/{id} Updates a specific employment # Create a faculty activity Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/create-a-faculty-activity /openapi.json post /api/v1/faculty_activities # Create a faculty activity for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/create-a-faculty-activity-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/faculty_activities # Delete a faculty activity Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/delete-a-faculty-activity /openapi.json delete /api/v1/faculty_activities/{id} Soft-deletes a faculty activity # Get a faculty activity Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/get-a-faculty-activity /openapi.json get /api/v1/faculty_activities/{id} Returns a single faculty activity by ID # List faculty activities Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/list-faculty-activities /openapi.json get /api/v1/faculty_activities Returns a list of faculty activities # List faculty activities for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/list-faculty-activities-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/faculty_activities Returns a list of faculty activities # Update a faculty activity Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activities/update-a-faculty-activity /openapi.json patch /api/v1/faculty_activities/{id} Updates a faculty activity # Get a faculty activity configuration Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activity-configurations/get-a-faculty-activity-configuration /openapi.json get /api/v1/faculty_activity_configurations/{id} Returns a faculty activity configuration # List faculty activity configurations Source: https://api-docs.scholarlysoftware.com/api-reference/faculty-activity-configurations/list-faculty-activity-configurations /openapi.json get /api/v1/faculty_activity_configurations Returns a list of faculty activity configurations # Confirm a file upload Source: https://api-docs.scholarlysoftware.com/api-reference/file-upload-requests/confirm-a-file-upload /openapi.json post /api/v1/file_upload_requests/{id}/confirm Step 3 of 3-step file upload: Confirm the upload by creating the actual file. After uploading the file to the direct upload URL (Step 2), call this endpoint with the `blob_signed_id` from the create response to create the file record. If the signed URL has expired, a 410 Gone response will be returned and you must initiate a new upload. Please see the [File Uploads guide](/file-uploads) for an overview of the whole process. # Create a file upload request in user's root folder Source: https://api-docs.scholarlysoftware.com/api-reference/file-upload-requests/create-a-file-upload-request-in-users-root-folder /openapi.json post /api/v1/file_upload_requests Step 1 of 3-step file upload: Initiate a direct upload. This endpoint creates a placeholder file record and returns credentials for uploading the file directly to the storage service. Please see the [File Uploads guide](/file-uploads) for an overview of the whole process. ## Response The response includes a `meta.direct_upload` object containing: - `url`: The URL to upload the file to - `headers`: Headers to include in the upload request - `blob_signed_id`: Signed ID of the blob (for reference) # Create a file upload request (initiate direct upload) Source: https://api-docs.scholarlysoftware.com/api-reference/file-upload-requests/create-a-file-upload-request-initiate-direct-upload /openapi.json post /api/v1/folders/{folder_id}/file_upload_requests Step 1 of 3-step file upload: Initiate a direct upload. This endpoint creates a placeholder file record and returns credentials for uploading the file directly to the storage service. Please see the [File Uploads guide](/file-uploads) for an overview of the whole process. ## Response The response includes a `meta.direct_upload` object containing: - `url`: The URL to upload the file to - `headers`: Headers to include in the upload request - `blob_signed_id`: Signed ID of the blob (for reference) # Delete a faculty activity file Source: https://api-docs.scholarlysoftware.com/api-reference/files/delete-a-faculty-activity-file /openapi.json delete /api/v1/faculty_activities/{faculty_activity_id}/files/{file_key} Removes a specific file from a faculty activity using the file's unique identifier # Delete a file Source: https://api-docs.scholarlysoftware.com/api-reference/files/delete-a-file /openapi.json delete /api/v1/files/{id} Deletes a file # Get a file Source: https://api-docs.scholarlysoftware.com/api-reference/files/get-a-file /openapi.json get /api/v1/files/{id} Returns details of a specific file # List faculty activity files Source: https://api-docs.scholarlysoftware.com/api-reference/files/list-faculty-activity-files /openapi.json get /api/v1/faculty_activities/{faculty_activity_id}/files Returns a list of files attached to a specific faculty activity in JSON:API format # List files for a folder Source: https://api-docs.scholarlysoftware.com/api-reference/files/list-files-for-a-folder /openapi.json get /api/v1/folders/{folder_id}/files # List files for an user Source: https://api-docs.scholarlysoftware.com/api-reference/files/list-files-for-an-user /openapi.json get /api/v1/users/{user_id}/files # Update a file Source: https://api-docs.scholarlysoftware.com/api-reference/files/update-a-file /openapi.json patch /api/v1/files/{id} Update a file's metadata or move it to a different folder. This endpoint can be used to: - Update the file name - Move the file to a different folder (must be owned by the same user) To confirm a direct upload, use POST /api/v1/files/:id/confirm instead. # Upload a faculty activity file Source: https://api-docs.scholarlysoftware.com/api-reference/files/upload-a-faculty-activity-file /openapi.json post /api/v1/faculty_activities/{faculty_activity_id}/files Uploads a file to a specific faculty activity by sending the raw file binary data in the request body # Create a folder Source: https://api-docs.scholarlysoftware.com/api-reference/folders/create-a-folder /openapi.json post /api/v1/folders Creates a new folder following JSON:API specification # Create a folder for an user Source: https://api-docs.scholarlysoftware.com/api-reference/folders/create-a-folder-for-an-user /openapi.json post /api/v1/users/{user_id}/folders Creates a new folder following JSON:API specification # Delete a folder Source: https://api-docs.scholarlysoftware.com/api-reference/folders/delete-a-folder /openapi.json delete /api/v1/folders/{id} Deletes a folder following JSON:API specification. This will also delete any files as well as subfolders and their files. # Get a folder Source: https://api-docs.scholarlysoftware.com/api-reference/folders/get-a-folder /openapi.json get /api/v1/folders/{id} Returns details of a specific folder # Get parent folder Source: https://api-docs.scholarlysoftware.com/api-reference/folders/get-parent-folder /openapi.json get /api/v1/folders/{id}/parent # Get parent relationship linkage Source: https://api-docs.scholarlysoftware.com/api-reference/folders/get-parent-relationship-linkage /openapi.json get /api/v1/folders/{id}/relationships/parent Returns JSON:API relationship linkage data for the parent folder. # List child folders Source: https://api-docs.scholarlysoftware.com/api-reference/folders/list-child-folders /openapi.json get /api/v1/folders/{id}/children # List children relationship linkage Source: https://api-docs.scholarlysoftware.com/api-reference/folders/list-children-relationship-linkage /openapi.json get /api/v1/folders/{id}/relationships/children Returns JSON:API relationship linkage data for child folders. # List folders Source: https://api-docs.scholarlysoftware.com/api-reference/folders/list-folders /openapi.json get /api/v1/folders Returns root folders for the current user. Folders are organized in a tree structure. This endpoint returns only root-level folders. Use the children endpoint to navigate the folder tree. # List folders for an user Source: https://api-docs.scholarlysoftware.com/api-reference/folders/list-folders-for-an-user /openapi.json get /api/v1/users/{user_id}/folders Returns root folders for the current user. Folders are organized in a tree structure. This endpoint returns only root-level folders. Use the children endpoint to navigate the folder tree. # Update a folder Source: https://api-docs.scholarlysoftware.com/api-reference/folders/update-a-folder /openapi.json patch /api/v1/folders/{id} Updates a folder's name following JSON:API specification # Update parent relationship Source: https://api-docs.scholarlysoftware.com/api-reference/folders/update-parent-relationship /openapi.json patch /api/v1/folders/{id}/relationships/parent Updates the parent relationship of a folder (JSON:API relationship endpoint). Use this to move folders. # Get an institution Source: https://api-docs.scholarlysoftware.com/api-reference/institutions/get-an-institution /openapi.json get /api/v1/institution Returns the current institution # Create a leave for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/create-a-leave-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/leaves # Delete a leave Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/delete-a-leave /openapi.json delete /api/v1/leaves/{id} Deletes a specific leave ### An important note on deleting leaves This resource is effective-dated in the Scholarly data model, which means you should almost-never delete a leave. Instead, you should update the `effective_to` property via updating a leave to signify that the leave has ended. # Get a leave Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/get-a-leave /openapi.json get /api/v1/leaves/{id} Returns a specific leave by UUID # List leaves Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/list-leaves /openapi.json get /api/v1/leaves Returns a list of leave records # List leaves for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/list-leaves-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/leaves Returns a list of leave records # Update a leave Source: https://api-docs.scholarlysoftware.com/api-reference/leaves/update-a-leave /openapi.json patch /api/v1/leaves/{id} Updates a specific leave # Create a phone number for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/create-a-phone-number-for-a-profile /openapi.json post /api/v1/profiles/{profile_id}/phone_numbers # Create a phone number for an user Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/create-a-phone-number-for-an-user /openapi.json post /api/v1/users/{user_id}/phone_numbers # Delete a phone number Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/delete-a-phone-number /openapi.json delete /api/v1/phone_numbers/{id} Deletes a specific phone number ### An important note on deleting phone numbers This resource is effective-dated in the Scholarly data model, which means you should almost-never delete a phone number. Instead, you should set the `effective_to` property via updating a phone number to signify that the phone number is no longer in use. # Get a phone number Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/get-a-phone-number /openapi.json get /api/v1/phone_numbers/{id} Returns a specific phone number by UUID # List phone numbers Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/list-phone-numbers /openapi.json get /api/v1/phone_numbers Returns a list of phone numbers # List phone numbers for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/list-phone-numbers-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/phone_numbers Returns a list of phone numbers # List phone numbers for an user Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/list-phone-numbers-for-an-user /openapi.json get /api/v1/users/{user_id}/phone_numbers Returns a list of phone numbers # Update a phone number Source: https://api-docs.scholarlysoftware.com/api-reference/phone-numbers/update-a-phone-number /openapi.json patch /api/v1/phone_numbers/{id} Updates a specific phone number # Create a primary appointment Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/create-a-primary-appointment /openapi.json post /api/v1/primary_appointments Create a new primary appointment # Delete a primary appointment Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/delete-a-primary-appointment /openapi.json delete /api/v1/primary_appointments/{id} Delete a primary appointment # Get a primary appointment Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/get-a-primary-appointment /openapi.json get /api/v1/primary_appointments/{id} Returns a specific primary appointment # List primary appointments Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/list-primary-appointments /openapi.json get /api/v1/primary_appointments Returns a list of primary appointments # List primary appointments for a profile Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/list-primary-appointments-for-a-profile /openapi.json get /api/v1/profiles/{profile_id}/primary_appointments # Update a primary appointment Source: https://api-docs.scholarlysoftware.com/api-reference/primary-appointments/update-a-primary-appointment /openapi.json patch /api/v1/primary_appointments/{id} Update a primary appointment # Create a profile Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/create-a-profile /openapi.json post /api/v1/profiles Non-admin users cannot create profiles # Delete a profile avatar Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/delete-a-profile-avatar /openapi.json delete /api/v1/profiles/{id}/avatar Delete the avatar image for a profile. This operation is idempotent - it will succeed even if no avatar exists. # Delete a profile CV Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/delete-a-profile-cv /openapi.json delete /api/v1/profiles/{id}/cv_upload Delete the CV for a profile. This operation is idempotent - it will succeed even if no CV exists. # Delete a profile preferred avatar Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/delete-a-profile-preferred-avatar /openapi.json delete /api/v1/profiles/{id}/preferred_avatar Delete the preferred avatar image for a profile. This operation is idempotent - it will succeed even if no preferred avatar exists. The synced avatar, if any, is not affected. # Get a profile Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/get-a-profile /openapi.json get /api/v1/profiles/{id} Returns a specific profile by UUID with relationships # List profiles Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/list-profiles /openapi.json get /api/v1/profiles Returns a list of profiles # Update a profile Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/update-a-profile /openapi.json patch /api/v1/profiles/{id} Update a profile's display name # Upload a profile avatar Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/upload-a-profile-avatar /openapi.json put /api/v1/profiles/{id}/avatar Upload an avatar image for a profile by sending the raw image binary data in the request body. This is the synced avatar, typically loaded from institutional data sources. When a profile also has a preferred avatar (uploaded by the user or via the preferred avatar endpoint), the preferred avatar takes precedence for display. # Upload a profile CV Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/upload-a-profile-cv /openapi.json put /api/v1/profiles/{id}/cv_upload Upload a CV PDF or DOCX for a profile by sending the raw binary data in the request body. Maximum file size is 10 MB. # Upload a profile preferred avatar Source: https://api-docs.scholarlysoftware.com/api-reference/profiles/upload-a-profile-preferred-avatar /openapi.json put /api/v1/profiles/{id}/preferred_avatar Upload a preferred avatar image for a profile by sending the raw image binary data in the request body. The preferred avatar is the user-managed photo — the same field written when a user uploads a photo in the web UI — and takes precedence over the synced avatar for display. # Create a rank Source: https://api-docs.scholarlysoftware.com/api-reference/ranks/create-a-rank /openapi.json post /api/v1/ranks Creates a new rank for the specified institution. Only available at institution admin permission levels. # Delete a rank Source: https://api-docs.scholarlysoftware.com/api-reference/ranks/delete-a-rank /openapi.json delete /api/v1/ranks/{id} Deletes a rank. Only available at institution admin permission levels. Only able to delete ranks with no primary appointments. # Get a rank Source: https://api-docs.scholarlysoftware.com/api-reference/ranks/get-a-rank /openapi.json get /api/v1/ranks/{id} Returns details of a specific rank # List ranks Source: https://api-docs.scholarlysoftware.com/api-reference/ranks/list-ranks /openapi.json get /api/v1/ranks Returns a list of ranks # Update a rank Source: https://api-docs.scholarlysoftware.com/api-reference/ranks/update-a-rank /openapi.json patch /api/v1/ranks/{id} Updates a rank. Only available at institution admin permission levels. # Create an user Source: https://api-docs.scholarlysoftware.com/api-reference/users/create-an-user /openapi.json post /api/v1/users Create a new user with a display name and email addresses # Get an user Source: https://api-docs.scholarlysoftware.com/api-reference/users/get-an-user /openapi.json get /api/v1/users/{id} Returns a single user by UUID # List users Source: https://api-docs.scholarlysoftware.com/api-reference/users/list-users /openapi.json get /api/v1/users Return a list of users # Update an user Source: https://api-docs.scholarlysoftware.com/api-reference/users/update-an-user /openapi.json patch /api/v1/users/{id} Updates an existing user # Additional appointment created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/additional_appointment-created /openapi.json webhook additional_appointment.created Triggered when a new additional appointment is created in an institution # Additional appointment deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/additional_appointment-deleted /openapi.json webhook additional_appointment.deleted Triggered when a additional appointment is deleted from an institution # Additional appointment updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/additional_appointment-updated /openapi.json webhook additional_appointment.updated Triggered when a additional appointment is updated in an institution # Backup completed Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/backup-completed /openapi.json webhook backup.completed Triggered when an institution backup export is completed # Department created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/department-created /openapi.json webhook department.created Triggered when a new department is created in an institution # Department deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/department-deleted /openapi.json webhook department.deleted Triggered when a department is deleted from an institution # Department updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/department-updated /openapi.json webhook department.updated Triggered when a department is updated in an institution # Faculty activity created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/faculty_activity-created /openapi.json webhook faculty_activity.created Triggered when a new faculty activity is created in an institution # Faculty activity deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/faculty_activity-deleted /openapi.json webhook faculty_activity.deleted Triggered when a faculty activity is deleted from an institution # Faculty activity updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/faculty_activity-updated /openapi.json webhook faculty_activity.updated Triggered when a faculty activity is updated in an institution # Primary appointment created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/primary_appointment-created /openapi.json webhook primary_appointment.created Triggered when a new primary appointment is created in an institution # Primary appointment deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/primary_appointment-deleted /openapi.json webhook primary_appointment.deleted Triggered when a primary appointment is deleted from an institution # Primary appointment updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/primary_appointment-updated /openapi.json webhook primary_appointment.updated Triggered when a primary appointment is updated in an institution # Profile created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/profile-created /openapi.json webhook profile.created Triggered when a new profile is created in an institution # Profile deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/profile-deleted /openapi.json webhook profile.deleted Triggered when a profile is deleted from an institution # Profile related object updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/profile-related_object_updated /openapi.json webhook profile.related_object_updated Triggered when a profile or any object in its graph (activities, appointments, leaves, custom fields, rank, department) changes. Typically, we recommend customers don't use this webhook and instead listen to the individual events on the specific models. # Profile updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/profile-updated /openapi.json webhook profile.updated Triggered when a profile is updated in an institution # Rank created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/rank-created /openapi.json webhook rank.created Triggered when a new rank is created in an institution # Rank deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/rank-deleted /openapi.json webhook rank.deleted Triggered when a rank is deleted from an institution # Rank updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/rank-updated /openapi.json webhook rank.updated Triggered when a rank is updated in an institution # User created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/user-created /openapi.json webhook user.created Triggered when a new user is created in an institution # User deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/user-deleted /openapi.json webhook user.deleted Triggered when a user is deleted from an institution # User updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/user-updated /openapi.json webhook user.updated Triggered when a user is updated in an institution, including changes to their custom fields # Workflows case completed Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/workflows_case-completed /openapi.json webhook workflows_case.completed Triggered when a workflow case is completed (all steps finished) # Workflows case created Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/workflows_case-created /openapi.json webhook workflows_case.created Triggered when a new workflow case is created # Workflows case deleted Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/workflows_case-deleted /openapi.json webhook workflows_case.deleted Triggered when a workflow case is deleted # Workflows case response completed Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/workflows_case-response_completed /openapi.json webhook workflows_case.response_completed Triggered when a step response is completed in a workflow case # Workflows case updated Source: https://api-docs.scholarlysoftware.com/api-reference/webhooks/workflows_case-updated /openapi.json webhook workflows_case.updated Triggered when a workflow case is updated # Get a workflow case Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-cases/get-a-workflow-case /openapi.json get /api/v1/workflow_cases/{id} Returns a specific case (faculty evaluation) by ID # List workflow cases Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-cases/list-workflow-cases /openapi.json get /api/v1/workflow_cases Returns a list of all cases (faculty evaluations) accessible to the user # List workflow cases for a workflow group Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-cases/list-workflow-cases-for-a-workflow-group /openapi.json get /api/v1/workflow_groups/{workflow_group_id}/workflow_cases Returns a list of all cases (faculty evaluations) accessible to the user # Get a workflow definition Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-definitions/get-a-workflow-definition /openapi.json get /api/v1/workflow_definitions/{id} Returns a specific workflow definition # List workflow definitions Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-definitions/list-workflow-definitions /openapi.json get /api/v1/workflow_definitions Returns a list of workflow definitions # Get a workflow group Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-groups/get-a-workflow-group /openapi.json get /api/v1/workflow_groups/{id} Returns a specific workflow group by ID # List children of a workflow group Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-groups/list-children-of-a-workflow-group /openapi.json get /api/v1/workflow_groups/{id}/children # List workflow groups for a workflow Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-groups/list-workflow-groups-for-a-workflow /openapi.json get /api/v1/workflows/{workflow_id}/workflow_groups # Get a workflow response Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-responses/get-a-workflow-response /openapi.json get /api/v1/workflow_responses/{id} Returns a specific workflow response by ID # List workflow responses Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-responses/list-workflow-responses /openapi.json get /api/v1/workflow_responses Returns a list of workflow responses. # List workflow responses for a workflow case Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-responses/list-workflow-responses-for-a-workflow-case /openapi.json get /api/v1/workflow_cases/{workflow_case_id}/workflow_responses Returns a list of workflow responses. # Get a workflow step Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-steps/get-a-workflow-step /openapi.json get /api/v1/workflow_steps/{id} Returns a specific workflow step by UUID # List workflow steps Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-steps/list-workflow-steps /openapi.json get /api/v1/workflow_steps Returns a list of workflow steps # List workflow steps for a workflow definition Source: https://api-docs.scholarlysoftware.com/api-reference/workflow-steps/list-workflow-steps-for-a-workflow-definition /openapi.json get /api/v1/workflow_definitions/{workflow_definition_id}/workflow_steps Returns a list of workflow steps # Get a workflow Source: https://api-docs.scholarlysoftware.com/api-reference/workflows/get-a-workflow /openapi.json get /api/v1/workflows/{id} Returns a specific workflow (faculty evaluation cycle) by UUID # List workflows Source: https://api-docs.scholarlysoftware.com/api-reference/workflows/list-workflows /openapi.json get /api/v1/workflows Returns a list of workflows (faculty evaluation cycles) # Filtering Faculty Activities by Integer Properties Source: https://api-docs.scholarlysoftware.com/faculty-activity-filters Bound your faculty activity queries to a numeric range over an integer property defined by your institution. The `GET /api/v1/faculty_activities` endpoint exposes range filters for every `integer`-kind property defined on your institution’s faculty activity configurations. This lets you pull, for example, a faculty member’s bar admissions for a year range, or publications above a citation count threshold — without iterating client-side. ## Filter shape Each integer property contributes six filter keys, one per comparison operator: | Operator | Filter key | Meaning | | -------- | ------------------------------- | ------------------------ | | `eq` | `filter[.eq]=N` | Equals | | `neq` | `filter[.neq]=N` | Does not equal | | `gt` | `filter[.gt]=N` | Greater than | | `gte` | `filter[.gte]=N` | Greater than or equal to | | `lt` | `filter[.lt]=N` | Less than | | `lte` | `filter[.lte]=N` | Less than or equal to | Combine `gte` + `lte` (or `gt` + `lt`) to express a range. ## Example Fetch bar admissions admitted between 2010 and 2022 (inclusive): ``` GET /api/v1/faculty_activities ?filter[key]=bar_admission &filter[year_admitted.gte]=2010 &filter[year_admitted.lte]=2022 ``` ## `filter[key]` is required Property names are scoped to your institution’s faculty activity configurations. To resolve a property unambiguously, every integer-property filter must be paired with `filter[key]=` so the API knows which configuration to interpret the property name against. The API responds with `400 Bad Request` if any of the following are true: * `filter[key]` is missing. * `filter[key]` points to a configuration that does not own the named integer property. * The filter value is not a valid integer. ## Caveats * Records whose JSON value for the property is `null` (or the property is absent) are excluded from `gte`/`lte`/`gt`/`lt`/`eq`/`neq` results. * Properties marked `allow_multiple` are not exposed through these filters. * The exact set of available property filters depends on how your institution has configured faculty activities. The `API reference` page is generated against a representative configuration; substitute your own property names in the URL pattern above. # File Uploads Source: https://api-docs.scholarlysoftware.com/file-uploads To support file uploads of any size and type, Scholarly uses a 3-step process for uploading files. Scholarly utilizes a novel, 3-step process for uploading files into a user's "My Documents" section which requires a few requests in sequence to successfully upload a file. Scholarly requires this to ensure that file uploads can complete, regardless of connection speed. Scholarly has customers send files directly to Amazon S3 using a secure method. This method is as follows: 0. [API clients create a file object and get credentials for the upload.](/api-reference/file-upload-requests/create-a-file-upload-request-initiate-direct-upload) The response contains authentication information for the next step. 1. API clients then upload the file directly to Amazon S3. 2. API clients then confirm the upload with a final request. By uploading the file directly to S3 in Step 2, this ensures the smooth operation of the Scholarly API servers. Here are some code snippets in different languages for handling this 3-step process. If you have trouble with this, please reach out to your Scholarly contact and we can pair program a solution in the language you're using. ```ruby scholarly_file_upload.rb theme={null} require "net/http" require "json" require "uri" require "digest" require "base64" class ScholarlyFileUploader BASE_URL = "https://api.scholarlysoftware.com" def initialize(api_token) @api_token = api_token end # Step 1: Initiate the direct upload def initiate_upload(folder_id:, file_path:, content_type: "application/octet-stream") file_content = File.binread(file_path) file_name = File.basename(file_path) file_size = file_content.bytesize checksum = Base64.strict_encode64(Digest::MD5.digest(file_content)) uri = URI("#{BASE_URL}/api/v1/folders/#{folder_id}/file_upload_requests") request_body = { data: { type: "file", attributes: { name: file_name, size: file_size, checksum: checksum, content_type: content_type } } } response = make_request(:post, uri, request_body) { file_id: response.dig("data", "id"), upload_url: response.dig("meta", "direct_upload", "url"), upload_headers: response.dig("meta", "direct_upload", "headers"), blob_signed_id: response.dig("meta", "direct_upload", "blob_signed_id"), file_content: file_content } end # Step 2: Upload file directly to S3 def upload_to_s3(upload_url:, upload_headers:, file_content:) uri = URI(upload_url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Put.new(uri) upload_headers.each { |key, value| request[key] = value } request["Content-Length"] = file_content.bytesize.to_s request.body = file_content response = http.request(request) unless response.is_a?(Net::HTTPSuccess) raise "S3 upload failed: #{response.code} - #{response.body}" end true end # Step 3: Confirm the upload def confirm_upload(file_id:, blob_signed_id:) uri = URI("#{BASE_URL}/api/v1/file_upload_requests/#{file_id}/confirm") request_body = {blob_signed_id: blob_signed_id} make_request(:post, uri, request_body) end # Convenience method to upload a file in one call def upload_file(folder_id:, file_path:, content_type: "application/octet-stream") # Step 1: Initiate upload_info = initiate_upload( folder_id: folder_id, file_path: file_path, content_type: content_type ) puts "Initiated upload for file ID: #{upload_info[:file_id]}" # Step 2: Upload to S3 upload_to_s3( upload_url: upload_info[:upload_url], upload_headers: upload_info[:upload_headers], file_content: upload_info[:file_content] ) puts "Uploaded to S3 successfully" # Step 3: Confirm result = confirm_upload( file_id: upload_info[:file_id], blob_signed_id: upload_info[:blob_signed_id] ) puts "Upload confirmed. File state: #{result.dig("data", "attributes", "state")}" result end private def make_request(method, uri, body = nil) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = case method when :post then Net::HTTP::Post.new(uri) when :get then Net::HTTP::Get.new(uri) when :put then Net::HTTP::Put.new(uri) end request["Authorization"] = "Bearer #{@api_token}" request["Content-Type"] = "application/vnd.api+json" request["Accept"] = "application/vnd.api+json" request.body = body.to_json if body response = http.request(request) unless response.is_a?(Net::HTTPSuccess) raise "API request failed: #{response.code} - #{response.body}" end JSON.parse(response.body) end end # Usage example: if __FILE__ == $0 api_token = ENV.fetch("SCHOLARLY_API_TOKEN") folder_id = "uuid-of-the-folder" file_path = "/path/to/your/file.md" uploader = ScholarlyFileUploader.new(api_token) begin result = uploader.upload_file( folder_id: folder_id, file_path: file_path, content_type: "text/plain" ) puts "\nUpload complete!" puts "File ID: #{result.dig("data", "id")}" puts "File URL: #{result.dig("data", "attributes", "url")}" puts "Download link: #{result.dig("data", "links", "download")}" rescue => e puts "Error: #{e.message}" exit 1 end end ``` ```javascript scholarly_file_upload.js theme={null} const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); class ScholarlyFileUploader { constructor(apiToken) { this.apiToken = apiToken; this.baseUrl = 'https://api.scholarlysoftware.com'; } // Step 1: Initiate the direct upload async initiateUpload({ folderId, filePath, contentType = 'application/octet-stream' }) { const fileContent = fs.readFileSync(filePath); const fileName = path.basename(filePath); const fileSize = fileContent.length; const checksum = crypto.createHash('md5').update(fileContent).digest('base64'); const requestBody = { data: { type: 'file', attributes: { name: fileName, size: fileSize, checksum: checksum, content_type: contentType } } }; const response = await this.makeRequest( 'POST', `/api/v1/folders/${folderId}/file_upload_requests`, requestBody ); return { fileId: response.data?.id, uploadUrl: response.meta?.direct_upload?.url, uploadHeaders: response.meta?.direct_upload?.headers, blobSignedId: response.meta?.direct_upload?.blob_signed_id, fileContent: fileContent }; } // Step 2: Upload file directly to S3 async uploadToS3({ uploadUrl, uploadHeaders, fileContent }) { const response = await fetch(uploadUrl, { method: 'PUT', headers: { ...uploadHeaders, 'Content-Length': fileContent.length.toString() }, body: fileContent }); if (!response.ok) { const errorText = await response.text(); throw new Error(`S3 upload failed: ${response.status} - ${errorText}`); } return true; } // Step 3: Confirm the upload async confirmUpload({ fileId, blobSignedId }) { const requestBody = { blob_signed_id: blobSignedId }; return await this.makeRequest( 'POST', `/api/v1/file_upload_requests/${fileId}/confirm`, requestBody ); } // Convenience method to upload a file in one call async uploadFile({ folderId, filePath, contentType = 'application/octet-stream' }) { // Step 1: Initiate const uploadInfo = await this.initiateUpload({ folderId, filePath, contentType }); console.log(`Initiated upload for file ID: ${uploadInfo.fileId}`); // Step 2: Upload to S3 await this.uploadToS3({ uploadUrl: uploadInfo.uploadUrl, uploadHeaders: uploadInfo.uploadHeaders, fileContent: uploadInfo.fileContent }); console.log('Uploaded to S3 successfully'); // Step 3: Confirm const result = await this.confirmUpload({ fileId: uploadInfo.fileId, blobSignedId: uploadInfo.blobSignedId }); console.log(`Upload confirmed. File state: ${result.data?.attributes?.state}`); return result; } async makeRequest(method, endpoint, body = null) { const url = `${this.baseUrl}${endpoint}`; const options = { method, headers: { 'Authorization': `Bearer ${this.apiToken}`, 'Content-Type': 'application/vnd.api+json', 'Accept': 'application/vnd.api+json' } }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); throw new Error(`API request failed: ${response.status} - ${errorText}`); } return await response.json(); } } async function nodeExample() { const apiToken = process.env.SCHOLARLY_API_TOKEN; const folderId = 'uuid-of-your-folder'; const filePath = '/path/to/your/file.md'; const uploader = new ScholarlyFileUploader(apiToken); try { const result = await uploader.uploadFile({ folderId, filePath, contentType: 'text/plain' }); console.log('\nUpload complete!'); console.log(`File ID: ${result.data?.id}`); console.log(`File URL: ${result.data?.attributes?.url}`); console.log(`Download link: ${result.data?.links?.download}`); } catch (error) { console.error('Error:', error.message); process.exit(1); } } ``` ```python scholarly_file_upload.py theme={null} import hashlib import base64 import json import os from pathlib import Path from typing import Optional import requests class ScholarlyFileUploader: def __init__(self, api_token: str): self.api_token = api_token self.base_url = "https://api.scholarlysoftware.com" def initiate_upload( self, folder_id: str, file_path: str, content_type: str = "application/octet-stream" ) -> dict: """Step 1: Initiate the direct upload.""" file_path = Path(file_path) file_content = file_path.read_bytes() file_name = file_path.name file_size = len(file_content) checksum = base64.b64encode(hashlib.md5(file_content).digest()).decode("utf-8") request_body = { "data": { "type": "file", "attributes": { "name": file_name, "size": file_size, "checksum": checksum, "content_type": content_type } } } response = self._make_request( "POST", f"/api/v1/folders/{folder_id}/file_upload_requests", request_body ) return { "file_id": response.get("data", {}).get("id"), "upload_url": response.get("meta", {}).get("direct_upload", {}).get("url"), "upload_headers": response.get("meta", {}).get("direct_upload", {}).get("headers"), "blob_signed_id": response.get("meta", {}).get("direct_upload", {}).get("blob_signed_id"), "file_content": file_content } def upload_to_s3( self, upload_url: str, upload_headers: dict, file_content: bytes ) -> bool: """Step 2: Upload file directly to S3.""" headers = { **upload_headers, "Content-Length": str(len(file_content)) } response = requests.put(upload_url, headers=headers, data=file_content) if not response.ok: raise Exception(f"S3 upload failed: {response.status_code} - {response.text}") return True def confirm_upload(self, file_id: str, blob_signed_id: str) -> dict: """Step 3: Confirm the upload.""" request_body = {"blob_signed_id": blob_signed_id} return self._make_request( "POST", f"/api/v1/file_upload_requests/{file_id}/confirm", request_body ) def upload_file( self, folder_id: str, file_path: str, content_type: str = "application/octet-stream" ) -> dict: """Convenience method to upload a file in one call.""" # Step 1: Initiate upload_info = self.initiate_upload( folder_id=folder_id, file_path=file_path, content_type=content_type ) print(f"Initiated upload for file ID: {upload_info['file_id']}") # Step 2: Upload to S3 self.upload_to_s3( upload_url=upload_info["upload_url"], upload_headers=upload_info["upload_headers"], file_content=upload_info["file_content"] ) print("Uploaded to S3 successfully") # Step 3: Confirm result = self.confirm_upload( file_id=upload_info["file_id"], blob_signed_id=upload_info["blob_signed_id"] ) state = result.get("data", {}).get("attributes", {}).get("state") print(f"Upload confirmed. File state: {state}") return result def _make_request( self, method: str, endpoint: str, body: Optional[dict] = None ) -> dict: url = f"{self.base_url}{endpoint}" headers = { "Authorization": f"Bearer {self.api_token}", "Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json" } response = requests.request( method=method, url=url, headers=headers, json=body ) if not response.ok: raise Exception(f"API request failed: {response.status_code} - {response.text}") return response.json() # Usage example if __name__ == "__main__": api_token = os.environ.get("SCHOLARLY_API_TOKEN") folder_id = "uuid-of-your-folder" file_path = "/path/to/your/file.md" uploader = ScholarlyFileUploader(api_token) try: result = uploader.upload_file( folder_id=folder_id, file_path=file_path, content_type="text/plain" ) print("\nUpload complete!") print(f"File ID: {result.get('data', {}).get('id')}") print(f"File URL: {result.get('data', {}).get('attributes', {}).get('url')}") print(f"Download link: {result.get('data', {}).get('links', {}).get('download')}") except Exception as e: print(f"Error: {e}") exit(1) ``` # Inclusion of Related Resources Source: https://api-docs.scholarlysoftware.com/include Including related resources are a great way to make the most efficient use of the Scholarly API Scholarly’s API is an implementation of the [JSON:API specification](https://jsonapi.org/). One of the optional parts of this specification is [Inclusion of Related Resources](https://jsonapi.org/format/#fetching-includes) via the `?include` query parameter. Scholarly implements this parameter on all GET endpoints. Bringing in these related resources is a great way to make fewer requests against the Scholarly API while still accomplishing your goal. Rather than needing to issue 1 request to fetch a list of users, and then N requests for their attached profiles, API clients are able to pull all of that down in one request. These extra records are written to the top-level `included` JSON key. Here’s an example response to the `GET /api/v1/users?include=profile` endpoint: ```json theme={null} { "data": [ /* Users data found here */ ], "included": [ /* Profile data found here */ ] } ``` ## Limitations To prevent runaway queries, Scholarly limits all `?include` parameters to a depth of `2`. Any request specifying more than that will receive a `400 Bad Request`. Note, a depth of `2` means the `?include` parameter will have at most 1 period (`.`). Here are a few examples: ``` # 200 OK GET /api/v1/users?include=profile GET /api/v1/users?include=profile.current_primary_appointment # 400 Bad Request GET /api/v1/users?include=profile.current_primary_appointment.rank ``` There is no limit to the number of 1- and 2-level relationships you can request: ``` # 200 OK GET /api/v1/users?include=profile,addresses,phone_numbers ``` # Introduction Source: https://api-docs.scholarlysoftware.com/index This is the introduction to Scholarly API Scholarly Logo Hello and welcome to the [Scholarly](https://scholarlysoftware.com/) API documentation. This site will provide an overview of how to interact with the Scholarly API. Using the Scholarly API is a great way to programmatically read or write data to your institution's Scholarly instance. The Scholarly API is an HTTPS API, which adheres to the [JSON:API specification](https://jsonapi.org/). This is a RESTful specification that optimizes for consistency across accessing different resources. ## Authentication The Scholarly API is authenticated using an API Key attached to a User record within Scholarly. Using this API Key confers all permissions associated with that user. Most of the time, these API Keys will be attached to service accounts with the "institution admin" permission. If you have questions or concerns, please reach out to your Scholarly rep. This API Key will always begin with `sch_`. Include this API Key in the `Authorization` header with the `Bearer ` prefix. Here's an example in curl: ```bash theme={null} curl https://api.scholarlysoftware.com/api/v1/departments \ -H "Authorization: Bearer sch_facebeef..." \ -H "Accept: application/vnd.api+json" ``` This will list all departments within your institution. ## Rate Limiting The Scholarly API defaults to 1 request per second per institution, unless otherwise negotiated within your contract. When the rate limit is exceeded, Scholarly will respond with `429 Too Many Requests`. We encourage you to handle these rate limits gracefully. ## Versioning The Scholarly API practices [Semantic Versioning 2.0.0](https://semver.org/) by encoding the major version in the path component, i.e. `/v1`. This means that any breaking changes will necessitate a bump in the version number. As a client, you can expect to never see breaking changes within the same version of the API. Scholarly classifies changes or removal of existing attribute or relationship behavior as breaking changes. Adding attributes or relationships to existing objects is not considered a breaking change. You may see payloads grow over time. ## IDs All resources within the Scholarly API will be identified by a UUID represented as a string. ## Read-on-write Consistency To ensure a performant experience for all customers and users, Scholarly employs the use of database replicas for serving read traffic in many situations. All `GET` requests in the Scholarly API will be served from a read replica. As a result, `GET` requests may experience replica lag leading to slightly stale data. Requests that write, i.e `PATCH`, `POST`, and `DELETE`, ensure read-on-write consistency. All endpoints will return the most up-to-date copy of the data. Let's take a look at a few examples: ``` # A single request that writes will receive the latest # data in its response POST /api/v1/departments # Receives a consistent copy # Compared with a write and then a read POST /api/v1/departments # and then... GET /api/v1/departments/{new_id} # May or may not receive the latest copy of data ``` The on-call Scholarly engineer gets paged if replica lag exceeds a certain threshold, but will be not respond to replica lag of a few seconds. Please design you API interactions with this read-on-write consistency in mind! ## Pagination The Scholarly API uses pagination to efficiently handle large datasets. All collection endpoints support pagination following the [JSON:API pagination specification](https://jsonapi.org/format/#fetching-pagination). To ensure consistent performance regardless of how deep in a list the query might be reaching, the Scholarly API implements cursor-based pagination. Rather than using a "limit + offset", you will set the page size and then be returned a cursor in the `links.next` attribute of the response. ### Request Parameters Pagination is controlled using the following query parameters: * `page[size]` - The number of items per page (default: 25, minimum: 1, maximum: 100) ### Example Requests ```bash theme={null} # Get the first page with default page size (25 items) GET /api/v1/departments # Inspect the response from above, pull out the `links.next` attribute. Get the next page GET /api/v1/departments?page[after]=...&page[size]=30 ``` ### Page Size Limits To ensure optimal performance: * **Default page size**: 25 items * **Minimum page size**: 1 item * **Maximum page size**: 100 items If you request a page size greater than 100, it will be automatically clamped to 100. ### Response Metadata Paginated responses include metadata about the collection in the `meta` field: ```json theme={null} { "data": [...], "meta": { "total": 250 // Total number of items in the collection // Additional pagination metadata }, "links": { // JSON:API pagination links when applicable } } ``` ### Best Practices 1. **Start with reasonable page sizes**: Use the default of 25 items unless you have specific requirements 2. **Handle empty results**: When iterating through pages, stop when you receive an empty `data` array 3. **Respect rate limits**: Pagination doesn't exempt you from rate limiting - space out your requests appropriately 4. **Use filters when possible**: Combine pagination with filtering to reduce the total number of pages needed ## AI-Native Scholarly's API docs are written to work well with large language models (LLMs) to make it easier to put together integration code. Click the "Copy page" button above to see the different options for working with Claude, ChatGPT, and more. In our experience, these LLMs do a great job of putting together the first draft of code to implement against Scholarly regardless of the programming language. ## Not Seeing a Resource? If you're not seeing a resource that you need to successfully integrate with Scholarly, please reach out to your rep. We can get the necessary endpoints created. # Webhooks Source: https://api-docs.scholarlysoftware.com/webhooks Webhooks are great for listening and responding to changes within Scholarly ## Overview Scholarly provides webhooks to notify your application when events occur in the system. Webhooks are HTTP POST requests sent to a URL you specify, containing information about the event that occurred. Webhooks are a great way to received information about changes in the Scholarly system near-instantaneously. This can be an important tool for keeping other tools or platforms in sync with Scholarly's data. Scholarly webhooks adhere to the [Standard Webhooks specification](https://www.standardwebhooks.com/). Webhooks can be created and configured by navigating to [Settings > Webhooks](https://app.scholarlysoftware.com/institution/settings/webhooks) in the Scholarly platform. ## Authentication All webhook requests include a signature in the `X-Webhook-Signature` header that you can use to verify the request came from Scholarly. The signature is an HMAC-SHA256 hash of the request body using your webhook's secret key. ### Verifying Webhook Signatures ```ruby verify_webhook_signature.rb theme={null} # Example webhook verification in Ruby def verify_webhook_signature(request) signature = request.headers['X-Webhook-Signature'] body = request.body.read expected_signature = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', webhook_secret, body)}" Rack::Utils.secure_compare(signature, expected_signature) end ``` ```javascript verifyWebhookSignature.js theme={null} // Example webhook verification in Node.js const crypto = require('crypto'); function verifyWebhookSignature(req, webhookSecret) { const signature = req.headers['x-webhook-signature']; const body = JSON.stringify(req.body); const expectedSignature = 'sha256=' + crypto .createHmac('sha256', webhookSecret) .update(body) .digest('hex'); return signature === expectedSignature; } ``` ## Webhook Headers All webhook requests include the following headers: | Header | Description | | --------------------- | ---------------------------------------------- | | `Content-Type` | Always `application/json` | | `User-Agent` | `Scholarly-Webhook/1.0` | | `X-Webhook-Id` | Unique identifier of the webhook configuration | | `X-Webhook-Event` | The type of event (e.g., `department.created`) | | `X-Webhook-Delivery` | Unique identifier for this delivery attempt | | `X-Webhook-Timestamp` | Unix timestamp of when the webhook was sent | | `X-Webhook-Signature` | HMAC-SHA256 signature of the request body | ## Webhook Payload Format All webhooks use a "thin" payload format that includes minimal information about the event. Your application should use the provided ID to fetch the full resource data via the appropriate API endpoint if needed. ```json theme={null} { "type": "event.type", "timestamp": "2025-01-20T10:30:00Z", "data": { "id": "resource-uuid" } } ``` ## Event Types Please see the `API Reference > Webhooks` section in the side bar for a list of all events and their structure. ## Retry Behavior If your webhook endpoint doesn't respond with a 2xx status code, Scholarly will retry the webhook with exponential backoff: * **Maximum retries:** 5 attempts * **Retry schedule:** * 1st retry: 10 seconds * 2nd retry: 20 seconds * 3rd retry: 40 seconds * 4th retry: 80 seconds (1.3 minutes) * 5th retry: 160 seconds (2.7 minutes, capped at 1 hour) After 5 failed attempts, the webhook delivery will be marked as failed and no further retries will be attempted. ## Response Requirements Your webhook endpoint should: 1. **Respond quickly** - Process the webhook asynchronously if needed. We recommend responding within 30 seconds. 2. **Return 2xx status** - Any 2xx status code (200-299) indicates successful receipt. 3. **Be idempotent** - The same webhook may be delivered multiple times due to retries. ## Example Webhook Handler ```ruby webhooks_controller.rb theme={null} # Ruby/Rails example class WebhooksController < ApplicationController skip_before_action :verify_authenticity_token def handle # Verify the webhook signature unless verify_webhook_signature(request) head :unauthorized return end # Parse the webhook payload event = JSON.parse(request.body.read) # Process the webhook asynchronously ProcessWebhookJob.perform_later(event) # Respond immediately head :ok end private def verify_webhook_signature(request) signature = request.headers['X-Webhook-Signature'] body = request.body.read request.body.rewind # Reset for re-reading expected = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', ENV['SCHOLARLY_WEBHOOK_SECRET'], body)}" Rack::Utils.secure_compare(signature, expected) end end ``` ```javascript server.js theme={null} // Node.js/Express example const express = require('express'); const crypto = require('crypto'); app.post('/webhooks', express.raw({type: 'application/json'}), (req, res) => { // Verify the webhook signature const signature = req.headers['x-webhook-signature']; const expectedSignature = 'sha256=' + crypto .createHmac('sha256', process.env.SCHOLARLY_WEBHOOK_SECRET) .update(req.body) .digest('hex'); if (signature !== expectedSignature) { return res.status(401).send('Unauthorized'); } // Parse the webhook payload const event = JSON.parse(req.body); // Process the webhook asynchronously processWebhook(event); // Respond immediately res.status(200).send('OK'); }); async function processWebhook(event) { switch(event.type) { case 'department.created': // Handle department creation const departmentId = event.data.id; // Fetch full department data from API if needed break; case 'profile.created': case 'profile.updated': case 'profile.deleted': // Handle profile events (created, updated, deleted) const profileId = event.data.id; // Note: profile.updated includes avatar changes // Fetch full profile data from API if needed break; case 'primary_appointment.created': case 'primary_appointment.updated': case 'primary_appointment.deleted': // Handle primary appointment events (created, updated, deleted) const primaryAppointmentId = event.data.id; // Fetch full primary appointment data from API if needed break; case 'additional_appointment.created': case 'additional_appointment.updated': case 'additional_appointment.deleted': // Handle secondary appointment events (created, updated, deleted) const secondaryAppointmentId = event.data.id; // Fetch full secondary appointment data from API if needed break; case 'user.created': case 'user.updated': case 'user.deleted': // Handle user events (created, updated, deleted) const userId = event.data.id; // Note: user.updated includes changes to the user's custom fields // Fetch full user data from API if needed break; // Handle other event types... } } ``` ## Rate Limits * Maximum of 100 concurrent webhook deliveries per institution * No limit on the number of webhooks per institution * No limit on the number of events per webhook ## Best Practices 1. **Use HTTPS** - Always use HTTPS endpoints for webhooks to ensure data security. 2. **Verify signatures** - Always verify webhook signatures to ensure authenticity. 3. **Handle duplicates** - Design your system to handle duplicate webhook deliveries idempotently. 4. **Process asynchronously** - Queue webhook processing to respond quickly. 5. **Monitor failures** - Set up monitoring for failed webhook deliveries. 6. **Use thin payloads** - Fetch full resource data via API when needed rather than relying on webhook payload. ## Testing Webhooks During development, you can use tools like: * [ngrok](https://ngrok.com/) - Expose your local server to receive webhooks * [webhook.site](https://webhook.site/) - Inspect webhook payloads * [RequestBin](https://requestbin.com/) - Create temporary webhook endpoints for testing ## Support For questions about webhooks or to report issues, please contact [support@scholarlysoftware.com](mailto:support@scholarlysoftware.com) or open an issue in your customer support channel.