diff --git a/src/api/users.rs b/src/api/users.rs
index d6e0769c..f5b38dbf 100644
--- a/src/api/users.rs
+++ b/src/api/users.rs
@@ -12,6 +12,7 @@ use crate::api::users::user_emails::UserEmailsOpsBuilder;
 use crate::api::users::user_git_ssh_keys::UserGitSshKeysOpsBuilder;
 use crate::api::users::user_gpg_keys::UserGpgKeysOpsBuilder;
 use crate::api::users::user_social_accounts::UserSocialAccountsOpsBuilder;
+use crate::api::users::user_ssh_signing_keys::UserSshSigningKeysOpsBuilder;
 use crate::models::UserId;
 use crate::params::users::emails::EmailVisibilityState;
 use crate::{error, GitHubError, Octocrab};
@@ -23,6 +24,7 @@ mod user_git_ssh_keys;
 mod user_gpg_keys;
 mod user_repos;
 mod user_social_accounts;
+mod user_ssh_signing_keys;
 
 pub(crate) enum UserRef {
     ByString(String),
@@ -212,4 +214,13 @@ impl<'octo> UserHandler<'octo> {
     pub fn social_accounts(&self) -> UserSocialAccountsOpsBuilder<'_, '_> {
         UserSocialAccountsOpsBuilder::new(self)
     }
+
+    ///SSH signing key administration
+    ///* List SSH signing keys for the authenticated user
+    ///* Create an SSH signing key for the authenticated user
+    ///* Get an SSH signing key for the authenticated user
+    ///* Delete an SSH signing key for the authenticated user
+    pub fn ssh_signing_keys(&self) -> UserSshSigningKeysOpsBuilder<'_, '_> {
+        UserSshSigningKeysOpsBuilder::new(self)
+    }
 }
diff --git a/src/api/users/user_ssh_signing_keys.rs b/src/api/users/user_ssh_signing_keys.rs
new file mode 100644
index 00000000..ca7c04e2
--- /dev/null
+++ b/src/api/users/user_ssh_signing_keys.rs
@@ -0,0 +1,152 @@
+use crate::api::users::UserHandler;
+use crate::models::SshSigningKey;
+use crate::{FromResponse, Page};
+
+#[derive(serde::Serialize)]
+pub struct UserSshSigningKeysOpsBuilder<'octo, 'b> {
+    #[serde(skip)]
+    handler: &'b UserHandler<'octo>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    per_page: Option<u8>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    page: Option<u32>,
+}
+
+impl<'octo, 'b> UserSshSigningKeysOpsBuilder<'octo, 'b> {
+    pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self {
+        Self {
+            handler,
+            per_page: None,
+            page: None,
+        }
+    }
+
+    /// Results per page (max 100).
+    pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
+        self.per_page = Some(per_page.into());
+        self
+    }
+
+    /// Page number of the results to fetch.
+    pub fn page(mut self, page: impl Into<u32>) -> Self {
+        self.page = Some(page.into());
+        self
+    }
+
+    ///## List SSH signing keys for the authenticated user
+    ///works with the following fine-grained token types:
+    ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
+    ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
+    ///
+    ///OAuth app tokens and personal access tokens (classic) need the `read:ssh_signing_key` scope
+    ///
+    ///The fine-grained token must have the following permission set:
+    ///* "SSH signing keys" user permissions (read)
+    ///
+    ///```no_run
+    ///  use octocrab::models::SshSigningKey;
+    /// use octocrab::{Page, Result};
+    ///  async fn run() -> Result<Page<SshSigningKey>> {
+    ///    octocrab::instance()
+    ///        .users("current_user")
+    ///        .ssh_signing_keys()
+    ///        .per_page(42).page(3u32)
+    ///        .list()
+    ///        .await
+    ///  }
+    pub async fn list(&self) -> crate::Result<Page<crate::models::SshSigningKey>> {
+        let route = "/user/ssh_signing_keys".to_string();
+        self.handler.crab.get(route, Some(&self)).await
+    }
+
+    ///## Get extended details for an SSH signing key for the authenticated user
+    ///
+    ///OAuth app tokens and personal access tokens (classic) need the `read:ssh_signing_key` scope to use this method.
+    ///
+    ///works with the following token types:
+    ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
+    ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
+    ///
+    ///The fine-grained token must have the following permission set:
+    ///* "SSH signing keys" user permissions (read)
+    ///
+    ///```no_run
+    ///  use octocrab::models::SshSigningKey;
+    /// use octocrab::Result;
+    ///  async fn run() -> Result<SshSigningKey> {
+    ///    octocrab::instance()
+    ///        .users("current_user")
+    ///        .ssh_signing_keys()
+    ///        .get(42)
+    ///        .await
+    ///  }
+    pub async fn get(&self, ssh_signing_key_id: u64) -> crate::Result<SshSigningKey> {
+        let route = format!("/user/ssh_signing_keys/{ssh_signing_key_id}");
+        self.handler.crab.get(route, None::<&()>).await
+    }
+
+    ///## Create a SSH signing key for the authenticated user
+    /// OAuth app tokens and personal access tokens (classic) need the `write:ssh_signing_key` scope to use this method.
+    ///
+    ///works with the following fine-grained token types:
+    ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
+    ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
+    ///
+    ///The fine-grained token must have the following permission set:
+    ///* "SSH signing keys" user permissions (write)
+    ///
+    ///```no_run
+    /// use octocrab::models::SshSigningKey;
+    /// use octocrab::Result;
+    ///  async fn run() -> Result<SshSigningKey> {
+    ///    octocrab::instance()
+    ///        .users("current_user")
+    ///        .ssh_signing_keys()
+    ///        .add("ssh-rsa AAAAB3NzaC1yc2EAAA".to_string(), "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234".to_string())
+    ///        .await
+    ///  }
+    pub async fn add(&self, title: String, key: String) -> crate::Result<SshSigningKey> {
+        let route = "/user/ssh_signing_keys".to_string();
+
+        let params = serde_json::json!({
+            "title": title,
+            "key": key,
+        });
+        let response = self.handler.crab._post(route, Some(&params)).await?;
+        if response.status() != http::StatusCode::CREATED {
+            return Err(crate::map_github_error(response).await.unwrap_err());
+        }
+
+        <SshSigningKey>::from_response(crate::map_github_error(response).await?).await
+    }
+
+    ///## Delete an SSH signing key for the authenticated user
+    /// OAuth app tokens and personal access tokens (classic) need the `admin:ssh_signing_key` scope
+    ///
+    ///works with the following fine-grained token types:
+    ///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
+    ///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
+    ///
+    ///The fine-grained token must have the following permission set:
+    ///* "SSH signing keys" user permissions (write)
+    ///
+    ///```no_run
+    /// use octocrab::Result;
+    ///  async fn run() -> Result<()> {
+    ///    octocrab::instance()
+    ///        .users("current_user")
+    ///        .ssh_signing_keys()
+    ///        .delete(42)
+    ///        .await
+    ///  }
+    pub async fn delete(&self, ssh_signing_key_id: u64) -> crate::Result<()> {
+        let route = format!("/user/ssh_signing_keys/{ssh_signing_key_id}");
+
+        let response = self.handler.crab._delete(route, None::<&()>).await?;
+        if response.status() != http::StatusCode::NO_CONTENT {
+            return Err(crate::map_github_error(response).await.unwrap_err());
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/models.rs b/src/models.rs
index 7c591a59..a75f4a2b 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -1176,3 +1176,11 @@ pub struct SocialAccount {
     pub provider: String,
     pub url: String,
 }
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct SshSigningKey {
+    pub key: String,
+    pub id: u64,
+    pub title: String,
+    pub created_at: DateTime<Utc>,
+}
diff --git a/tests/resources/user_ssh_signing_key_created.json b/tests/resources/user_ssh_signing_key_created.json
new file mode 100644
index 00000000..0887c0cb
--- /dev/null
+++ b/tests/resources/user_ssh_signing_key_created.json
@@ -0,0 +1,7 @@
+{
+  "key": "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234",
+  "id": 2,
+  "url": "https://api.github.com/user/keys/2",
+  "title": "ssh-rsa AAAAB3NzaC1yc2EAAA",
+  "created_at": "2020-06-11T21:31:57Z"
+}
diff --git a/tests/resources/user_ssh_signing_keys.json b/tests/resources/user_ssh_signing_keys.json
new file mode 100644
index 00000000..3dbf1f69
--- /dev/null
+++ b/tests/resources/user_ssh_signing_keys.json
@@ -0,0 +1,16 @@
+[
+  {
+    "key": "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234",
+    "id": 2,
+    "url": "https://api.github.com/user/keys/2",
+    "title": "ssh-rsa AAAAB3NzaC1yc2EAAA",
+    "created_at": "2020-06-11T21:31:57Z"
+  },
+  {
+    "key": "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJy931234",
+    "id": 3,
+    "url": "https://api.github.com/user/keys/3",
+    "title": "ssh-rsa AAAAB3NzaC1yc2EAAB",
+    "created_at": "2020-07-11T21:31:57Z"
+  }
+]
diff --git a/tests/user_ssh_signing_keys_tests.rs b/tests/user_ssh_signing_keys_tests.rs
new file mode 100644
index 00000000..504e0de1
--- /dev/null
+++ b/tests/user_ssh_signing_keys_tests.rs
@@ -0,0 +1,135 @@
+use http::StatusCode;
+use wiremock::{
+    matchers::{method, path},
+    Mock, MockServer, ResponseTemplate,
+};
+
+use mock_error::setup_error_handler;
+use octocrab::models::SshSigningKey;
+use octocrab::Octocrab;
+
+/// Tests API calls related to check runs of a specific commit.
+mod mock_error;
+
+const SSH_SIGNING_KEY_ID: u64 = 42;
+
+async fn setup_ssh_signing_keys_mock(
+    http_method: &str,
+    mocked_path: &str,
+    template: ResponseTemplate,
+) -> MockServer {
+    let mock_server = MockServer::start().await;
+
+    Mock::given(method(http_method))
+        .and(path(mocked_path))
+        .respond_with(template.clone())
+        .mount(&mock_server)
+        .await;
+    setup_error_handler(
+        &mock_server,
+        &format!("http method {http_method} on {mocked_path} was not received"),
+    )
+    .await;
+    mock_server
+}
+
+fn setup_octocrab(uri: &str) -> Octocrab {
+    Octocrab::builder().base_uri(uri).unwrap().build().unwrap()
+}
+
+#[tokio::test]
+async fn should_respond_to_get_ssh_signing_key() {
+    let mocked_response: SshSigningKey =
+        serde_json::from_str(include_str!("resources/user_ssh_signing_key_created.json")).unwrap();
+    let template = ResponseTemplate::new(200).set_body_json(&mocked_response);
+    let mock_server = setup_ssh_signing_keys_mock(
+        "GET",
+        format!("/user/ssh_signing_keys/{SSH_SIGNING_KEY_ID}").as_str(),
+        template,
+    )
+    .await;
+    let client = setup_octocrab(&mock_server.uri());
+    let result = client
+        .users("some_other_user")
+        .ssh_signing_keys()
+        .get(SSH_SIGNING_KEY_ID)
+        .await;
+    assert!(
+        result.is_ok(),
+        "expected successful result, got error: {:#?}",
+        result
+    );
+    let response = result.unwrap();
+    let id = response.id;
+    assert_eq!(id, 2);
+}
+
+#[tokio::test]
+async fn should_respond_to_ssh_signing_keys_list() {
+    let mocked_response: Vec<SshSigningKey> =
+        serde_json::from_str(include_str!("resources/user_ssh_signing_keys.json")).unwrap();
+    let template = ResponseTemplate::new(200).set_body_json(&mocked_response);
+    let mock_server = setup_ssh_signing_keys_mock("GET", "/user/ssh_signing_keys", template).await;
+    let client = setup_octocrab(&mock_server.uri());
+    let result = client
+        .users("some_other_user")
+        .ssh_signing_keys()
+        .per_page(42)
+        .page(3u32)
+        .list()
+        .await;
+    assert!(
+        result.is_ok(),
+        "expected successful result, got error: {:#?}",
+        result
+    );
+    let response = result.unwrap();
+    let id = response.items.first().unwrap().id;
+    assert_eq!(id, 2);
+}
+
+#[tokio::test]
+async fn should_respond_to_ssh_signing_keys_add() {
+    let mocked_response: SshSigningKey =
+        serde_json::from_str(include_str!("resources/user_ssh_signing_key_created.json")).unwrap();
+    let template = ResponseTemplate::new(StatusCode::CREATED).set_body_json(&mocked_response);
+    let mock_server = setup_ssh_signing_keys_mock("POST", "/user/ssh_signing_keys", template).await;
+    let client = setup_octocrab(&mock_server.uri());
+    let result = client
+        .users("some_user")
+        .ssh_signing_keys()
+        .add(
+            "Assh-rsa AAAAB3NzaC1yc2EAA".to_string(),
+            "A2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv123".to_string(),
+        )
+        .await;
+    assert!(
+        result.is_ok(),
+        "expected successful result, got error: {:#?}",
+        result
+    );
+    let result = result.unwrap();
+    assert_eq!(result.id, 2);
+}
+
+#[tokio::test]
+async fn should_respond_to_ssh_signing_key_delete() {
+    let template = ResponseTemplate::new(StatusCode::NO_CONTENT);
+    let mock_server = setup_ssh_signing_keys_mock(
+        "DELETE",
+        format!("/user/ssh_signing_keys/{SSH_SIGNING_KEY_ID}").as_str(),
+        template,
+    )
+    .await;
+    let client = setup_octocrab(&mock_server.uri());
+    let result = client
+        .users("some_user")
+        .ssh_signing_keys()
+        .delete(SSH_SIGNING_KEY_ID)
+        .await;
+    assert!(
+        result.is_ok(),
+        "expected successful result, got error: {:#?}",
+        result
+    );
+}